Leptonica 1.68
C Image Processing Library

colorquant2.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  *  colorquant2.c
00018  *                     
00019  *  Modified median cut color quantization
00020  *
00021  *      High level
00022  *          PIX              *pixMedianCutQuant()
00023  *          PIX              *pixMedianCutQuantGeneral()
00024  *          PIX              *pixMedianCutQuantMixed()
00025  *          PIX              *pixFewColorsMedianCutQuantMixed()
00026  *
00027  *      Median cut indexed histogram
00028  *          l_int32          *pixMedianCutHisto()
00029  *
00030  *      Static helpers
00031  *          static PIXCMAP   *pixcmapGenerateFromHisto()
00032  *          static PIX       *pixQuantizeWithColormap()
00033  *          static void       getColorIndexMedianCut()
00034  *          static L_BOX3D   *pixGetColorRegion()
00035  *          static l_int32    medianCutApply()
00036  *          static PIXCMAP   *pixcmapGenerateFromMedianCuts()
00037  *          static l_int32    vboxGetAverageColor()
00038  *          static l_int32    vboxGetCount()
00039  *          static l_int32    vboxGetVolume()
00040  *          static L_BOX3D   *box3dCreate();
00041  *          static L_BOX3D   *box3dCopy();
00042  *
00043  *   Paul Heckbert published the median cut algorithm, "Color Image
00044  *   Quantization for Frame Buffer Display," in Proc. SIGGRAPH '82,
00045  *   Boston, July 1982, pp. 297-307.  A copy of the paper without
00046  *   figures can be found on the web.
00047  *
00048  *   Median cut starts with either the full color space or the occupied
00049  *   region of color space.  If you're not dithering, the occupied region
00050  *   can be used, but with dithering, pixels can end up in any place
00051  *   in the color space, so you must represent the entire color space in
00052  *   the final colormap.
00053  *
00054  *   Color components are quantized to typically 5 or 6 significant
00055  *   bits (for each of r, g and b).   Call a 3D region of color
00056  *   space a 'vbox'.  Any color in this quantized space is represented
00057  *   by an element of a linear histogram array, indexed by rgb value.
00058  *   The initial region is then divided into two regions that have roughly
00059  *   equal pixel occupancy (hence the name "median cut").  Subdivision
00060  *   continues until the requisite number of vboxes has been generated.
00061  *
00062  *   But the devil is in the details of the subdivision process.
00063  *   Here are some choices that you must make:
00064  *     (1) Along which axis to subdivide?
00065  *     (2) Which box to put the bin with the median pixel?
00066  *     (3) How to order the boxes for subdivision?
00067  *     (4) How to adequately handle boxes with very small numbers of pixels?
00068  *     (5) How to prevent a little-represented but highly visible color
00069  *         from being masked out by other colors in its vbox.
00070  *
00071  *   Taking these in order:
00072  *     (1) Heckbert suggests using either the largest vbox side, or the vbox
00073  *         side with the largest variance in pixel occupancy.  We choose
00074  *         to divide based on the largest vbox side.
00075  *     (2) Suppose you've chosen a side.  Then you have a histogram
00076  *         of pixel occupancy in 2D slices of the vbox.  One of those
00077  *         slices includes the median pixel.  Suppose there are L bins
00078  *         to the left (smaller index) and R bins to the right.  Then
00079  *         this slice (or bin) should be assigned to the box containing
00080  *         the smaller of L and R.  This both shortens the larger
00081  *         of the subdivided dimensions and helps a low-count color
00082  *         far from the subdivision boundary to better express itself.
00083  *     (2a) One can also ask if the boundary should be moved even
00084  *         farther into the longer side.  This is feasable if we have
00085  *         a method for doing extra subdivisions on the high count
00086  *         vboxes.  And we do (see (3)).
00087  *     (3) To make sure that the boxes are subdivided toward equal
00088  *         occupancy, use an occupancy-sorted priority queue, rather
00089  *         than a simple queue.
00090  *     (4) With a priority queue, boxes with small number of pixels
00091  *         won't be repeatedly subdivided.  This is good.
00092  *     (5) Use of a priority queue allows tricks such as in (2a) to let
00093  *         small occupancy clusters be better expressed.  In addition,
00094  *         rather than splitting near the median, small occupancy colors
00095  *         are best reproduced by cutting half-way into the longer side.
00096  *
00097  *   However, serious problems can arise with dithering if a priority
00098  *   queue is used based on population alone.  If the picture has
00099  *   large regions of nearly constant color, some vboxes can be very
00100  *   large and have a sizeable population (but not big enough to get to
00101  *   the head of the queue).  If one of these large, occupied vboxes
00102  *   is near in color to a nearly constant color region of the
00103  *   image, dithering can inject pixels from the large vbox into
00104  *   the nearly uniform region.  These pixels can be very far away
00105  *   in color, and the oscillations are highly visible.  To prevent
00106  *   this, we can take either or both of these actions:
00107  *
00108  *     (1) Subdivide a fraction (< 1.0) based on population, and
00109  *         do the rest of the subdivision based on the product of
00110  *         the vbox volume and its population.  By using the product,
00111  *         we avoid further subdivision of nearly empty vboxes, and
00112  *         directly target large vboxes with significant population.
00113  *
00114  *     (2) Threshold the excess color transferred in dithering to
00115  *         neighboring pixels.
00116  *
00117  *   Doing either of these will stop the most annoying oscillations
00118  *   in dithering.  Furthermore, by doing (1), we also improve the
00119  *   rendering of regions of nearly constant color, both with and
00120  *   without dithering.  It turns out that the image quality is
00121  *   not sensitive to the value of the parameter in (1); values
00122  *   between 0.3 and 0.9 give very good results.
00123  *
00124  *   Here's the lesson: subdivide the color space into vboxes such
00125  *   that (1) the most populated vboxes that can be further
00126  *   subdivided (i.e., that occupy more than one quantum volume
00127  *   in color space) all have approximately the same population,
00128  *   and (2) all large vboxes have no significant population.
00129  *   If these conditions are met, the quantization will be excellent.
00130  *
00131  *   Once the subdivision has been made, the colormap is generated,
00132  *   with one color for each vbox and using the average color in the vbox.
00133  *   At the same time, the histogram array is converted to an inverse
00134  *   colormap table, storing the colormap index in every cell in the
00135  *   vbox.  Finally, using both the colormap and the inverse colormap,
00136  *   a colormapped pix is quickly generated from the original rgb pix.
00137  *
00138  *   In the present implementation, subdivided regions of colorspace
00139  *   that are not occupied are retained, but not further subdivided.
00140  *   This is required for our inverse colormap lookup table for
00141  *   dithering, because dithered pixels may fall into these unoccupied
00142  *   regions.  For such empty regions, we use the center as the rgb
00143  *   colormap value.
00144  *
00145  *   This variation on median cut can be referred to as "Modified Median
00146  *   Cut" quantization, or MMCQ.  Overall, the undithered MMCQ gives
00147  *   comparable results to the two-pass Octcube Quantizer (OQ).
00148  *   Comparing the two methods on the test24.jpg painting, we see:
00149  *
00150  *     (1) For rendering spot color (the various reds and pinks in
00151  *         the image), MMCQ is not as good as OQ.
00152  *
00153  *     (2) For rendering majority color regions, MMCQ does a better
00154  *         job of avoiding posterization.  That is, it does better
00155  *         dividing the color space up in the most heavily populated regions.
00156  */
00157 
00158 #include <stdio.h>
00159 #include <stdlib.h>
00160 #include <string.h>
00161 #include <math.h>
00162 #include "allheaders.h"
00163 
00164     /* Median cut 3-d volume element.  Sort on first element, which
00165      * can be the number of pixels, the volume or a combination
00166      * of these.   */
00167 struct L_Box3d
00168 {
00169     l_float32        sortparam;  /* parameter on which to sort the vbox */
00170     l_int32          npix;       /* number of pixels in the vbox        */
00171     l_int32          vol;        /* quantized volume of vbox            */
00172     l_int32          r1;         /* min r index in the vbox             */
00173     l_int32          r2;         /* max r index in the vbox             */
00174     l_int32          g1;         /* min g index in the vbox             */
00175     l_int32          g2;         /* max g index in the vbox             */
00176     l_int32          b1;         /* min b index in the vbox             */
00177     l_int32          b2;         /* max b index in the vbox             */
00178 };
00179 typedef struct L_Box3d  L_BOX3D;
00180 
00181     /* Static median cut helper functions */
00182 static PIXCMAP *pixcmapGenerateFromHisto(PIX *pixs, l_int32 depth,
00183                                          l_int32 *histo, l_int32 histosize,
00184                                          l_int32 sigbits);
00185 static PIX *pixQuantizeWithColormap(PIX *pixs, l_int32 ditherflag,
00186                                     l_int32 outdepth,
00187                                     PIXCMAP *cmap, l_int32 *indexmap,
00188                                     l_int32 mapsize, l_int32 sigbits);
00189 static void getColorIndexMedianCut(l_uint32 pixel, l_int32 rshift,
00190                                    l_uint32 mask, l_int32 sigbits,
00191                                    l_int32 *pindex);
00192 static L_BOX3D *pixGetColorRegion(PIX *pixs, l_int32 sigbits,
00193                                   l_int32 subsample);
00194 static l_int32 medianCutApply(l_int32 *histo, l_int32 sigbits,
00195                               L_BOX3D *vbox, L_BOX3D **pvbox1,
00196                               L_BOX3D **pvbox2);
00197 static PIXCMAP *pixcmapGenerateFromMedianCuts(L_HEAP *lh, l_int32 *histo,
00198                                               l_int32 sigbits);
00199 static l_int32 vboxGetAverageColor(L_BOX3D *vbox, l_int32 *histo,
00200                                    l_int32 sigbits, l_int32 index,
00201                                    l_int32  *prval, l_int32 *pgval,
00202                                    l_int32  *pbval);
00203 static l_int32 vboxGetCount(L_BOX3D *vbox, l_int32 *histo, l_int32 sigbits);
00204 static l_int32 vboxGetVolume(L_BOX3D *vbox);
00205 static L_BOX3D *box3dCreate(l_int32 r1, l_int32 r2, l_int32 g1,
00206                             l_int32 g2, l_int32 b1, l_int32 b2);
00207 static L_BOX3D *box3dCopy(L_BOX3D *vbox);
00208 
00209 
00210     /* 5 significant bits for each component is generally satisfactory */
00211 static const l_int32  DEFAULT_SIG_BITS = 5;
00212 static const l_int32  MAX_ITERS_ALLOWED = 5000;  /* prevents infinite looping */
00213 
00214     /* Specify fraction of vboxes made that are sorted on population alone.
00215      * The remaining vboxes are sorted on (population * vbox-volume).  */
00216 static const l_float32  FRACT_BY_POPULATION = 0.85;
00217 
00218     /* To get the max value of 'dif' in the dithering color transfer,
00219      * divide DIF_CAP by 8. */
00220 static const l_int32  DIF_CAP = 100;
00221 
00222 
00223 #ifndef   NO_CONSOLE_IO
00224 #define   DEBUG_MC_COLORS       0
00225 #define   DEBUG_SPLIT_AXES      0
00226 #endif   /* ~NO_CONSOLE_IO */
00227 
00228 
00229 /*------------------------------------------------------------------------*
00230  *                                 High level                             *
00231  *------------------------------------------------------------------------*/
00232 /*!
00233  *  pixMedianCutQuant()
00234  *
00235  *      Input:  pixs  (32 bpp; rgb color)
00236  *              ditherflag (1 for dither; 0 for no dither)
00237  *      Return: pixd (8 bit with colormap), or null on error
00238  *
00239  *  Notes:
00240  *      (1) Simple interface.  See pixMedianCutQuantGeneral() for
00241  *          use of defaulted parameters.
00242  */ 
00243 PIX *
00244 pixMedianCutQuant(PIX     *pixs,
00245                   l_int32  ditherflag)
00246 {
00247     return pixMedianCutQuantGeneral(pixs, ditherflag,
00248                                     0, 256, DEFAULT_SIG_BITS, 1, 1);
00249 }
00250 
00251 
00252 /*!
00253  *  pixMedianCutQuantGeneral()
00254  *
00255  *      Input:  pixs  (32 bpp; rgb color)
00256  *              ditherflag (1 for dither; 0 for no dither)
00257  *              outdepth (output depth; valid: 0, 1, 2, 4, 8)
00258  *              maxcolors (between 2 and 256)
00259  *              sigbits (valid: 5 or 6; use 0 for default)
00260  *              maxsub (max subsampling, integer; use 0 for default;
00261  *                      1 for no subsampling)
00262  *              checkbw (1 to check if color content is very small,
00263  *                       0 to assume there is sufficient color)
00264  *      Return: pixd (8 bit with colormap), or null on error
00265  *
00266  *  Notes:
00267  *      (1) @maxcolors must be in the range [2 ... 256].
00268  *      (2) Use @outdepth = 0 to have the output depth computed as the
00269  *          minimum required to hold the actual colors found, given
00270  *          the @maxcolors constraint.
00271  *      (3) Use @outdepth = 1, 2, 4 or 8 to specify the output depth.
00272  *          In that case, @maxcolors must not exceed 2^(outdepth).
00273  *      (4) If there are fewer quantized colors in the image than @maxcolors,
00274  *          the colormap is simply generated from those colors.
00275  *      (5) @maxsub is the maximum allowed subsampling to be used in the
00276  *          computation of the color histogram and region of occupied
00277  *          color space.  The subsampling is chosen internally for
00278  *          efficiency, based on the image size, but this parameter
00279  *          limits it.  Use @maxsub = 0 for the internal default, which is the
00280  *          maximum allowed subsampling.  Use @maxsub = 1 to prevent
00281  *          subsampling.  In general use @maxsub >= 1 to specify the
00282  *          maximum subsampling to be allowed, where the actual subsampling
00283  *          will be the minimum of this value and the internally
00284  *          determined default value.
00285  *      (6) If the image appears gray because either most of the pixels
00286  *          are gray or most of the pixels are essentially black or white,
00287  *          the image is trivially quantized with a grayscale colormap.  The
00288  *          reason is that median cut divides the color space into rectangular
00289  *          regions, and it does a very poor job if all the pixels are
00290  *          near the diagonal of the color space cube.
00291  */ 
00292 PIX *
00293 pixMedianCutQuantGeneral(PIX     *pixs,
00294                          l_int32  ditherflag,
00295                          l_int32  outdepth,
00296                          l_int32  maxcolors,
00297                          l_int32  sigbits,
00298                          l_int32  maxsub,
00299                          l_int32  checkbw)
00300 {
00301 l_int32    i, subsample, histosize, smalln, ncolors, niters, popcolors;
00302 l_int32    w, h, minside, factor, index, rval, gval, bval;
00303 l_int32   *histo;
00304 l_float32  pixfract, colorfract;
00305 L_BOX3D   *vbox, *vbox1, *vbox2;
00306 L_HEAP    *lh, *lhs;
00307 PIX       *pixd;
00308 PIXCMAP   *cmap;
00309 
00310     PROCNAME("pixMedianCutQuantGeneral");
00311 
00312     if (!pixs || pixGetDepth(pixs) != 32)
00313         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
00314     if (maxcolors < 2 || maxcolors > 256)
00315         return (PIX *)ERROR_PTR("maxcolors not in [2...256]", procName, NULL);
00316     if (outdepth != 0 && outdepth != 1 && outdepth != 2 && outdepth != 4 &&
00317         outdepth != 8)
00318         return (PIX *)ERROR_PTR("outdepth not in {0,1,2,4,8}", procName, NULL);
00319     if (outdepth > 0 && (maxcolors > (1 << outdepth)))
00320         return (PIX *)ERROR_PTR("maxcolors > 2^(outdepth)", procName, NULL);
00321     if (sigbits == 0)
00322         sigbits = DEFAULT_SIG_BITS;
00323     else if (sigbits < 5 || sigbits > 6)
00324         return (PIX *)ERROR_PTR("sigbits not 5 or 6", procName, NULL);
00325     if (maxsub <= 0)
00326         maxsub = 10;  /* default will prevail for 10^7 pixels or less */
00327 
00328         /* Determine if the image has sufficient color content.
00329          * If pixfract << 1, most pixels are close to black or white.
00330          * If colorfract << 1, the pixels that are not near
00331          *   black or white have very little color.
00332          * If with little color, quantize with a grayscale colormap. */
00333     pixGetDimensions(pixs, &w, &h, NULL);
00334     if (checkbw) {
00335         minside = L_MIN(w, h);
00336         factor = L_MAX(1, minside / 400);
00337         pixColorFraction(pixs, 20, 244, 20, factor, &pixfract, &colorfract);
00338         if (pixfract * colorfract < 0.00025) {
00339             L_INFO_FLOAT2("\n  Pixel fraction neither white nor black = %6.3f"
00340                           "\n  Color fraction of those pixels = %6.3f"
00341                           "\n  Quantizing in gray",
00342                           procName, pixfract, colorfract);
00343             return pixConvertTo8(pixs, 1);
00344         }
00345     }
00346 
00347         /* Compute the color space histogram.  Default sampling
00348          * is about 10^5 pixels.  */
00349     if (maxsub == 1)
00350         subsample = 1;
00351     else {
00352         subsample = (l_int32)(sqrt((l_float64)(w * h) / 100000.));
00353         subsample = L_MAX(1, L_MIN(maxsub, subsample));
00354     }
00355     histo = pixMedianCutHisto(pixs, sigbits, subsample);
00356     histosize = 1 << (3 * sigbits);
00357 
00358         /* See if the number of quantized colors is less than maxcolors */
00359     ncolors = 0;
00360     smalln = TRUE;
00361     for (i = 0; i < histosize; i++) {
00362         if (histo[i])
00363             ncolors++;
00364         if (ncolors > maxcolors) {
00365             smalln = FALSE;
00366             break;
00367         }
00368     }
00369     if (smalln) {  /* finish up now */
00370         if (outdepth == 0) {
00371             if (ncolors <= 2)
00372                 outdepth = 1;
00373             else if (ncolors <= 4)
00374                 outdepth = 2;
00375             else if (ncolors <= 16)
00376                 outdepth = 4;
00377             else 
00378                 outdepth = 8;
00379         }
00380         cmap = pixcmapGenerateFromHisto(pixs, outdepth,
00381                                         histo, histosize, sigbits);
00382         pixd = pixQuantizeWithColormap(pixs, ditherflag, outdepth, cmap,
00383                                        histo, histosize, sigbits);
00384         FREE(histo);
00385         return pixd;
00386     }
00387 
00388         /* Initial vbox: minimum region in colorspace occupied by pixels */
00389     if (ditherflag || subsample > 1)  /* use full color space */
00390         vbox = box3dCreate(0, (1 << sigbits) - 1,
00391                            0, (1 << sigbits) - 1,
00392                            0, (1 << sigbits) - 1);
00393     else
00394         vbox = pixGetColorRegion(pixs, sigbits, subsample);
00395     vbox->npix = vboxGetCount(vbox, histo, sigbits);
00396     vbox->vol = vboxGetVolume(vbox);
00397 
00398         /* For a fraction 'popcolors' of the desired 'maxcolors',
00399          * generate median cuts based on population, putting
00400          * everything on a priority queue sorted by population. */
00401     lh = lheapCreate(0, L_SORT_DECREASING);
00402     lheapAdd(lh, vbox);
00403     ncolors = 1;
00404     niters = 0;
00405     popcolors = (l_int32)(FRACT_BY_POPULATION * maxcolors);
00406     while (1) {
00407         vbox = (L_BOX3D *)lheapRemove(lh);
00408         if (vboxGetCount(vbox, histo, sigbits) == 0)  { /* just put it back */
00409             lheapAdd(lh, vbox);
00410             continue;
00411         }
00412         medianCutApply(histo, sigbits, vbox, &vbox1, &vbox2);
00413         if (!vbox1) {
00414             L_WARNING("vbox1 not defined; shouldn't happen!", procName);
00415             break;
00416         }
00417         if (vbox1->vol > 1)
00418             vbox1->sortparam = vbox1->npix;
00419         FREE(vbox);
00420         lheapAdd(lh, vbox1);
00421         if (vbox2) {  /* vbox2 can be NULL */
00422             if (vbox2->vol > 1)
00423                 vbox2->sortparam = vbox2->npix;
00424             lheapAdd(lh, vbox2);
00425             ncolors++;
00426         }
00427         if (ncolors >= popcolors)
00428             break;
00429         if (niters++ > MAX_ITERS_ALLOWED) {
00430             L_WARNING("infinite loop; perhaps too few pixels!", procName);
00431             break;
00432         }
00433     }
00434 
00435         /* Re-sort by the product of pixel occupancy times the size
00436          * in color space. */
00437     lhs = lheapCreate(0, L_SORT_DECREASING);
00438     while ((vbox = (L_BOX3D *)lheapRemove(lh))) {
00439         vbox->sortparam = vbox->npix * vbox->vol;
00440         lheapAdd(lhs, vbox);
00441     }
00442     lheapDestroy(&lh, TRUE);
00443 
00444         /* For the remaining (maxcolors - popcolors), generate the
00445          * median cuts using the (npix * vol) sorting. */
00446     while (1) {
00447         vbox = (L_BOX3D *)lheapRemove(lhs);
00448         if (vboxGetCount(vbox, histo, sigbits) == 0)  { /* just put it back */
00449             lheapAdd(lhs, vbox);
00450             continue;
00451         }
00452         medianCutApply(histo, sigbits, vbox, &vbox1, &vbox2);
00453         if (!vbox1) {
00454             L_WARNING("vbox1 not defined; shouldn't happen!", procName);
00455             break;
00456         }
00457         if (vbox1->vol > 1)
00458             vbox1->sortparam = vbox1->npix * vbox1->vol;
00459         FREE(vbox);
00460         lheapAdd(lhs, vbox1);
00461         if (vbox2) {  /* vbox2 can be NULL */
00462             if (vbox2->vol > 1)
00463                 vbox2->sortparam = vbox2->npix * vbox2->vol;
00464             lheapAdd(lhs, vbox2);
00465             ncolors++;
00466         }
00467         if (ncolors >= maxcolors)
00468             break;
00469         if (niters++ > MAX_ITERS_ALLOWED) {
00470             L_WARNING("infinite loop; perhaps too few pixels!", procName);
00471             break;
00472         }
00473     }
00474 
00475         /* Re-sort by pixel occupancy.  This is not necessary,
00476          * but it makes a more useful listing.  */
00477     lh = lheapCreate(0, L_SORT_DECREASING);
00478     while ((vbox = (L_BOX3D *)lheapRemove(lhs))) {
00479         vbox->sortparam = vbox->npix;
00480 /*        vbox->sortparam = vbox->npix * vbox->vol; */
00481         lheapAdd(lh, vbox);
00482     }
00483     lheapDestroy(&lhs, TRUE);
00484 
00485         /* Generate colormap from median cuts and quantize pixd */
00486     cmap = pixcmapGenerateFromMedianCuts(lh, histo, sigbits);
00487     if (outdepth == 0) {
00488         ncolors = pixcmapGetCount(cmap);
00489         if (ncolors <= 2)
00490             outdepth = 1;
00491         else if (ncolors <= 4)
00492             outdepth = 2;
00493         else if (ncolors <= 16)
00494             outdepth = 4;
00495         else 
00496             outdepth = 8;
00497     }
00498     pixd = pixQuantizeWithColormap(pixs, ditherflag, outdepth, cmap,
00499                                    histo, histosize, sigbits);
00500 
00501         /* Force darkest color to black if each component <= 4 */
00502     pixcmapGetRankIntensity(cmap, 0.0, &index);
00503     pixcmapGetColor(cmap, index, &rval, &gval, &bval);
00504     if (rval < 5 && gval < 5 && bval < 5)
00505         pixcmapResetColor(cmap, index, 0, 0, 0);
00506 
00507         /* Force lightest color to white if each component >= 252 */
00508     pixcmapGetRankIntensity(cmap, 1.0, &index);
00509     pixcmapGetColor(cmap, index, &rval, &gval, &bval);
00510     if (rval > 251 && gval > 251 && bval > 251)
00511         pixcmapResetColor(cmap, index, 255, 255, 255);
00512 
00513     lheapDestroy(&lh, TRUE);
00514     FREE(histo);
00515     return pixd;
00516 }
00517 
00518 
00519 /*!
00520  *  pixMedianCutQuantMixed()
00521  *
00522  *      Input:  pixs  (32 bpp; rgb color)
00523  *              ncolor (maximum number of colors assigned to pixels with
00524  *                      significant color)
00525  *              ngray (number of gray colors to be used; must be >= 2)
00526  *              darkthresh (threshold near black; if the lightest component
00527  *                          is below this, the pixel is not considered to
00528  *                          be gray or color; uses 0 for default)
00529  *              lightthresh (threshold near white; if the darkest component
00530  *                           is above this, the pixel is not considered to
00531  *                           be gray or color; use 0 for default)
00532  *              diffthresh (thresh for the max difference between component
00533  *                          values; for differences below this, the pixel
00534  *                          is considered to be gray; use 0 for default)
00535  *      Return: pixd (8 bpp cmapped), or null on error
00536  *
00537  *  Notes:
00538  *      (1) ncolor + ngray must not exceed 255.
00539  *      (2) The method makes use of pixMedianCutQuantGeneral() with
00540  *          minimal addition.
00541  *          (a) Preprocess the image, setting all pixels with little color
00542  *              to black, and populating an auxiliary 8 bpp image with the
00543  *              expected colormap values corresponding to the set of
00544  *              quantized gray values.
00545  *          (b) Color quantize the altered input image to n + 1 colors.
00546  *          (c) Augment the colormap with the gray indices, and
00547  *              substitute the gray quantized values from the auxiliary
00548  *              image for those in the color quantized output that had
00549  *              been quantized as black.
00550  *      (3) Median cut color quantization is relatively poor for grayscale
00551  *          images with many colors, when compared to octcube quantization.
00552  *          Thus, for images with both gray and color, it is important
00553  *          to quantize the gray pixels by another method.  Here, we
00554  *          are conservative in detecting color, preferring to use
00555  *          a few extra bits to encode colorful pixels that push them
00556  *          to gray.  This is particularly reasonable with this function,
00557  *          because it handles the gray and color pixels separately,
00558  *          using median cut color quantization for the color pixels
00559  *          and equal-bin grayscale quantization for the non-color pixels.
00560  */
00561 PIX *
00562 pixMedianCutQuantMixed(PIX     *pixs,
00563                        l_int32  ncolor,
00564                        l_int32  ngray,
00565                        l_int32  darkthresh,
00566                        l_int32  lightthresh,
00567                        l_int32  diffthresh)
00568 {
00569 l_int32    i, j, w, h, wplc, wplg, wpld, nc, unused, iscolor, factor, minside;
00570 l_int32    rval, gval, bval, minval, maxval, val, grayval;
00571 l_float32  pixfract, colorfract;
00572 l_int32   *lut;
00573 l_uint32  *datac, *datag, *datad, *linec, *lineg, *lined;
00574 PIX       *pixc, *pixg, *pixd;
00575 PIXCMAP   *cmap;
00576 
00577     PROCNAME("pixMedianCutQuantMixed");
00578 
00579     if (!pixs || pixGetDepth(pixs) != 32)
00580         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
00581     if (ngray < 2)
00582         return (PIX *)ERROR_PTR("ngray < 2", procName, NULL);
00583     if (ncolor + ngray > 255)
00584         return (PIX *)ERROR_PTR("ncolor + ngray > 255", procName, NULL);
00585     if (darkthresh <= 0) darkthresh = 20;
00586     if (lightthresh <= 0) lightthresh = 244;
00587     if (diffthresh <= 0) diffthresh = 20;
00588 
00589         /* First check if this should be quantized in gray.
00590          * Use a more sensitive parameter for detecting color than with
00591          * pixMedianCutQuantGeneral(), because this function can handle
00592          * gray pixels well.  */
00593     pixGetDimensions(pixs, &w, &h, NULL);
00594     minside = L_MIN(w, h);
00595     factor = L_MAX(1, minside / 400);
00596     pixColorFraction(pixs, darkthresh, lightthresh, diffthresh, factor,
00597                      &pixfract, &colorfract);
00598     if (pixfract * colorfract < 0.0001) {
00599         L_INFO_FLOAT2("\n  Pixel fraction neither white nor black = %6.3f"
00600                       "\n  Color fraction of those pixels = %6.3f"
00601                       "\n  Quantizing in gray",
00602                       procName, pixfract, colorfract);
00603         pixg = pixConvertTo8(pixs, 0);
00604         pixd = pixThresholdOn8bpp(pixg, ngray, 1);
00605         pixDestroy(&pixg);
00606         return pixd;
00607     }
00608 
00609         /* OK, there is color in the image.
00610          * Preprocess to handle the gray pixels.  Set the color pixels in pixc
00611          * to black, and store their (eventual) colormap indices in pixg.*/
00612     pixc = pixCopy(NULL, pixs);
00613     pixg = pixCreate(w, h, 8);  /* color pixels will remain 0 here */
00614     datac = pixGetData(pixc);
00615     datag = pixGetData(pixg);
00616     wplc = pixGetWpl(pixc);
00617     wplg = pixGetWpl(pixg);
00618     lut = (l_int32 *)CALLOC(256, sizeof(l_int32));
00619     for (i = 0; i < 256; i++)
00620         lut[i] = ncolor + 1 + (i * (ngray - 1) + 128) / 255;
00621     for (i = 0; i < h; i++) {
00622         linec = datac + i * wplc;
00623         lineg = datag + i * wplg;
00624         for (j = 0; j < w; j++) {
00625             iscolor = FALSE;
00626             extractRGBValues(linec[j], &rval, &gval, &bval);
00627             minval = L_MIN(rval, gval);
00628             minval = L_MIN(minval, bval);
00629             maxval = L_MAX(rval, gval);
00630             maxval = L_MAX(maxval, bval);
00631             if (maxval >= darkthresh &&
00632                 minval <= lightthresh &&
00633                 maxval - minval >= diffthresh) {
00634                 iscolor = TRUE;
00635             }
00636             if (!iscolor) {
00637                 linec[j] = 0x0;  /* set to black */
00638                 grayval = (maxval + minval) / 2;
00639                 SET_DATA_BYTE(lineg, j, lut[grayval]);
00640             }
00641         }
00642     }
00643 
00644         /* Median cut on color pixels plus black */
00645     pixd = pixMedianCutQuantGeneral(pixc, FALSE, 8, ncolor + 1,
00646                                     DEFAULT_SIG_BITS, 1, 0);
00647 
00648         /* Augment the colormap with gray values.  The new cmap
00649          * indices should agree with the values previously stored in pixg. */
00650     cmap = pixGetColormap(pixd);
00651     nc = pixcmapGetCount(cmap);
00652     unused = ncolor  + 1 - nc;
00653     if (unused < 0)
00654         L_ERROR_INT("Too many colors: extra = %d", procName, -unused);
00655     if (unused > 0) {  /* fill in with black; these won't be used */
00656         L_INFO_INT("%d unused colors", procName, unused);
00657         for (i = 0; i < unused; i++)
00658             pixcmapAddColor(cmap, 0, 0, 0);
00659     }
00660     for (i = 0; i < ngray; i++) {
00661         grayval = (255 * i) / (ngray - 1);
00662         pixcmapAddColor(cmap, grayval, grayval, grayval);
00663     }
00664 
00665         /* Substitute cmap indices for the gray pixels into pixd */
00666     datad = pixGetData(pixd);
00667     wpld = pixGetWpl(pixd);
00668     for (i = 0; i < h; i++) {
00669         lined = datad + i * wpld;
00670         lineg = datag + i * wplg;
00671         for (j = 0; j < w; j++) {
00672             val = GET_DATA_BYTE(lineg, j);  /* if 0, it's a color pixel */
00673             if (val)
00674                 SET_DATA_BYTE(lined, j, val);
00675         }
00676     }
00677 
00678     pixDestroy(&pixc);
00679     pixDestroy(&pixg);
00680     FREE(lut);
00681     return pixd;
00682 }
00683 
00684 
00685 /*!
00686  *  pixFewColorsMedianCutQuantMixed()
00687  *
00688  *      Input:  pixs (32 bpp rgb)
00689  *              ncolor (number of colors to be assigned to pixels with
00690  *                       significant color)
00691  *              ngray (number of gray colors to be used; must be >= 2)
00692  *              maxncolors (maximum number of colors to be returned
00693  *                         from pixColorsForQuantization(); use 0 for default)
00694  *              darkthresh (threshold near black; if the lightest component
00695  *                          is below this, the pixel is not considered to
00696  *                          be gray or color; use 0 for default)
00697  *              lightthresh (threshold near white; if the darkest component
00698  *                           is above this, the pixel is not considered to
00699  *                           be gray or color; use 0 for default)
00700  *              diffthresh (thresh for the max difference between component
00701  *                          values; for differences below this, the pixel
00702  *                          is considered to be gray; use 0 for default)
00703  *                          considered gray; use 0 for default)
00704  *      Return: pixd (8 bpp, median cut quantized for pixels that are
00705  *                    not gray; gray pixels are quantized separately
00706  *                    over the full gray range); null if too many colors
00707  *                    or on error
00708  *
00709  *  Notes:
00710  *      (1) This is the "few colors" version of pixMedianCutQuantMixed().
00711  *          It fails (returns NULL) if it finds more than maxncolors, but
00712  *          otherwise it gives the same result.
00713  *      (2) Recommended input parameters are:
00714  *              @maxncolors:  20
00715  *              @darkthresh:  20
00716  *              @lightthresh: 244
00717  *              @diffthresh:  15  (any higher can miss colors differing
00718  *                                 slightly from gray)
00719  *      (3) Both ncolor and ngray should be at least equal to maxncolors.
00720  *          If they're not, they are automatically increased, and a
00721  *          warning is given.
00722  *      (4) If very little color content is found, the input is
00723  *          converted to gray and quantized in equal intervals.
00724  *      (5) This can be useful for quantizing orthographically generated
00725  *          images such as color maps, where there may be more than 256 colors
00726  *          because of aliasing or jpeg artifacts on text or lines, but
00727  *          there are a relatively small number of solid colors.
00728  *      (6) Example of usage:
00729  *             // Try to quantize, using default values for mixed med cut
00730  *             Pix *pixq = pixFewColorsMedianCutQuantMixed(pixs, 100, 20,
00731  *                             0, 0, 0, 0);
00732  *             if (!pixq)  // too many colors; don't quantize
00733  *                 pixq = pixClone(pixs);
00734  */
00735 PIX *
00736 pixFewColorsMedianCutQuantMixed(PIX       *pixs,
00737                                 l_int32    ncolor,
00738                                 l_int32    ngray,
00739                                 l_int32    maxncolors,
00740                                 l_int32    darkthresh,
00741                                 l_int32    lightthresh,
00742                                 l_int32    diffthresh)
00743 {
00744 l_int32  ncolors, iscolor;
00745 PIX     *pixg, *pixd;
00746 
00747     PROCNAME("pixFewColorsMedianCutQuantMixed");
00748 
00749     if (!pixs || pixGetDepth(pixs) != 32)
00750         return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL);
00751     if (maxncolors <= 0) maxncolors = 20;
00752     if (darkthresh <= 0) darkthresh = 20;
00753     if (lightthresh <= 0) lightthresh = 244;
00754     if (diffthresh <= 0) diffthresh = 15;
00755     if (ncolor < maxncolors) {
00756         L_WARNING_INT("ncolor too small; setting to %d", procName, maxncolors);
00757         ncolor = maxncolors;
00758     }
00759     if (ngray < maxncolors) {
00760         L_WARNING_INT("ngray too small; setting to %d", procName, maxncolors);
00761         ngray = maxncolors;
00762     }
00763 
00764         /* Estimate the color content and the number of colors required */
00765     pixColorsForQuantization(pixs, 15, &ncolors, &iscolor, 0);
00766 
00767         /* Note that maxncolors applies to all colors required to quantize,
00768          * both gray and colorful */
00769     if (ncolors > maxncolors)
00770         return (PIX *)ERROR_PTR("too many colors", procName, NULL);
00771 
00772         /* If no color, return quantized gray pix */
00773     if (!iscolor) {
00774         pixg = pixConvertTo8(pixs, 0);
00775         pixd = pixThresholdOn8bpp(pixg, ngray, 1);
00776         pixDestroy(&pixg);
00777         return pixd;
00778     }
00779 
00780         /* Use the mixed gray/color quantizer */
00781     return pixMedianCutQuantMixed(pixs, ncolor, ngray, darkthresh,
00782                                   lightthresh, diffthresh);
00783 }
00784 
00785 
00786 
00787 /*------------------------------------------------------------------------*
00788  *                        Median cut indexed histogram                    *
00789  *------------------------------------------------------------------------*/
00790 /*!
00791  *  pixMedianCutHisto()
00792  *
00793  *      Input:  pixs  (32 bpp; rgb color)
00794  *              sigbits (valid: 5 or 6)
00795  *              subsample (integer > 0)
00796  *      Return: histo (1-d array, giving the number of pixels in
00797  *                     each quantized region of color space), or null on error
00798  *
00799  *  Notes:
00800  *      (1) Array is indexed by (3 * sigbits) bits.  The array size
00801  *          is 2^(3 * sigbits).
00802  *      (2) Indexing into the array from rgb uses red sigbits as
00803  *          most significant and blue as least.
00804  */
00805 l_int32 *
00806 pixMedianCutHisto(PIX     *pixs,
00807                   l_int32  sigbits,
00808                   l_int32  subsample)
00809 {
00810 l_int32    i, j, w, h, wpl, rshift, index, histosize;
00811 l_int32   *histo;
00812 l_uint32   mask, pixel;
00813 l_uint32  *data, *line;
00814 
00815     PROCNAME("pixMedianCutHisto");
00816 
00817     if (!pixs)
00818         return (l_int32 *)ERROR_PTR("pixs not defined", procName, NULL);
00819     if (pixGetDepth(pixs) != 32)
00820         return (l_int32 *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
00821     if (sigbits < 5 || sigbits > 6)
00822         return (l_int32 *)ERROR_PTR("sigbits not 5 or 6", procName, NULL);
00823     if (subsample <= 0)
00824         return (l_int32 *)ERROR_PTR("subsample not > 0", procName, NULL);
00825 
00826     histosize = 1 << (3 * sigbits);
00827     if ((histo = (l_int32 *)CALLOC(histosize, sizeof(l_int32))) == NULL)
00828         return (l_int32 *)ERROR_PTR("histo not made", procName, NULL);
00829 
00830     rshift = 8 - sigbits;
00831     mask = 0xff >> rshift;
00832     pixGetDimensions(pixs, &w, &h, NULL);
00833     data = pixGetData(pixs);
00834     wpl = pixGetWpl(pixs);
00835     for (i = 0; i < h; i += subsample) {
00836         line = data + i * wpl; 
00837         for (j = 0; j < w; j += subsample) {
00838             pixel = line[j];
00839             getColorIndexMedianCut(pixel, rshift, mask, sigbits, &index);
00840             histo[index]++;
00841         }
00842     }
00843 
00844     return histo;
00845 }
00846 
00847 
00848 /*------------------------------------------------------------------------*
00849  *                               Static helpers                           *
00850  *------------------------------------------------------------------------*/
00851 /*!
00852  *  pixcmapGenerateFromHisto()
00853  *
00854  *      Input:  pixs  (32 bpp; rgb color)
00855  *              depth (of colormap)
00856  *              histo
00857  *              histosize
00858  *              sigbits
00859  *      Return: colormap, or null on error
00860  *
00861  *  Notes:
00862  *      (1) This is used when the number of colors in the histo
00863  *          is not greater than maxcolors.
00864  *      (2) As a side-effect, the histo becomes an inverse colormap,
00865  *          labeling the cmap indices for each existing color.
00866  */
00867 static PIXCMAP *
00868 pixcmapGenerateFromHisto(PIX      *pixs,
00869                          l_int32   depth,
00870                          l_int32  *histo,
00871                          l_int32   histosize,
00872                          l_int32   sigbits)
00873 {
00874 l_int32   i, index, shift, rval, gval, bval;
00875 l_uint32  mask;
00876 PIXCMAP  *cmap;
00877 
00878     PROCNAME("pixcmapGenerateFromHisto");
00879 
00880     if (!pixs)
00881         return (PIXCMAP *)ERROR_PTR("pixs not defined", procName, NULL);
00882     if (pixGetDepth(pixs) != 32)
00883         return (PIXCMAP *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
00884     if (!histo)
00885         return (PIXCMAP *)ERROR_PTR("histo not defined", procName, NULL);
00886 
00887         /* Capture the rgb values of each occupied cube in the histo,
00888          * and re-label the histo value with the colormap index. */
00889     cmap = pixcmapCreate(depth);
00890     shift = 8 - sigbits;
00891     mask = 0xff >> shift;
00892     for (i = 0, index = 0; i < histosize; i++) {
00893         if (histo[i]) {
00894             rval = (i >> (2 * sigbits)) << shift;
00895             gval = ((i >> sigbits) & mask) << shift;
00896             bval = (i & mask) << shift;
00897             pixcmapAddColor(cmap, rval, gval, bval);
00898             histo[i] = index++;
00899         }
00900     }
00901 
00902     return cmap;
00903 }
00904 
00905 
00906 /*!
00907  *  pixQuantizeWithColormap()
00908  *
00909  *      Input:  pixs  (32 bpp; rgb color)
00910  *              ditherflag (1 for dither; 0 for no dither)
00911  *              outdepth
00912  *              cmap
00913  *              indexmap
00914  *              histosize
00915  *              sigbits
00916  *      Return: pixd (quantized to colormap), or null on error
00917  *
00918  *  Notes:
00919  *      (1) The indexmap is a LUT that takes the rgb indices of the
00920  *          pixel and returns the index into the colormap.
00921  *      (2) If ditherflag is 1, @outdepth is ignored and the output
00922  *          depth is set to 8.
00923  */
00924 static PIX *
00925 pixQuantizeWithColormap(PIX      *pixs,
00926                         l_int32   ditherflag,
00927                         l_int32   outdepth,
00928                         PIXCMAP  *cmap,
00929                         l_int32  *indexmap,
00930                         l_int32   mapsize,
00931                         l_int32   sigbits)
00932 {
00933 l_uint8   *bufu8r, *bufu8g, *bufu8b;
00934 l_int32    i, j, w, h, wpls, wpld, rshift, index, cmapindex;
00935 l_int32    rval, gval, bval, rc, gc, bc;
00936 l_int32    dif, val1, val2, val3;
00937 l_int32   *buf1r, *buf1g, *buf1b, *buf2r, *buf2g, *buf2b;
00938 l_uint32  *datas, *datad, *lines, *lined;
00939 l_uint32   mask, pixel;
00940 PIX       *pixd;
00941 
00942     PROCNAME("pixQuantizeWithColormap");
00943 
00944     if (!pixs || pixGetDepth(pixs) != 32)
00945         return (PIX *)ERROR_PTR("pixs not 32 bpp", procName, NULL);
00946     if (!cmap)
00947         return (PIX *)ERROR_PTR("cmap not defined", procName, NULL);
00948     if (!indexmap)
00949         return (PIX *)ERROR_PTR("indexmap not defined", procName, NULL);
00950     if (ditherflag)
00951         outdepth = 8;
00952 
00953     pixGetDimensions(pixs, &w, &h, NULL);
00954     pixd = pixCreate(w, h, outdepth);
00955     pixSetColormap(pixd, cmap);
00956     pixCopyResolution(pixd, pixs);
00957     pixCopyInputFormat(pixd, pixs);
00958     datas = pixGetData(pixs);
00959     datad = pixGetData(pixd);
00960     wpls = pixGetWpl(pixs);
00961     wpld = pixGetWpl(pixd);
00962 
00963     rshift = 8 - sigbits;
00964     mask = 0xff >> rshift;
00965     if (ditherflag == 0) {
00966         for (i = 0; i < h; i++) {
00967             lines = datas + i * wpls;
00968             lined = datad + i * wpld;
00969             if (outdepth == 1) {
00970                 for (j = 0; j < w; j++) {
00971                     pixel = lines[j];
00972                     getColorIndexMedianCut(pixel, rshift, mask,
00973                                            sigbits, &index);
00974                     if (indexmap[index])
00975                         SET_DATA_BIT(lined, j);
00976                 }
00977             }
00978             else if (outdepth == 2) {
00979                 for (j = 0; j < w; j++) {
00980                     pixel = lines[j];
00981                     getColorIndexMedianCut(pixel, rshift, mask,
00982                                            sigbits, &index);
00983                     SET_DATA_DIBIT(lined, j, indexmap[index]);
00984                 }
00985             }
00986             else if (outdepth == 4) {
00987                 for (j = 0; j < w; j++) {
00988                     pixel = lines[j];
00989                     getColorIndexMedianCut(pixel, rshift, mask,
00990                                            sigbits, &index);
00991                     SET_DATA_QBIT(lined, j, indexmap[index]);
00992                 }
00993             }
00994             else {  /* outdepth == 8 */
00995                 for (j = 0; j < w; j++) {
00996                     pixel = lines[j];
00997                     getColorIndexMedianCut(pixel, rshift, mask,
00998                                            sigbits, &index);
00999                     SET_DATA_BYTE(lined, j, indexmap[index]);
01000                 }
01001             }
01002         }
01003     }
01004     else {  /* ditherflag == 1 */
01005         bufu8r = (l_uint8 *)CALLOC(w, sizeof(l_uint8));
01006         bufu8g = (l_uint8 *)CALLOC(w, sizeof(l_uint8));
01007         bufu8b = (l_uint8 *)CALLOC(w, sizeof(l_uint8));
01008         buf1r = (l_int32 *)CALLOC(w, sizeof(l_int32));
01009         buf1g = (l_int32 *)CALLOC(w, sizeof(l_int32));
01010         buf1b = (l_int32 *)CALLOC(w, sizeof(l_int32));
01011         buf2r = (l_int32 *)CALLOC(w, sizeof(l_int32));
01012         buf2g = (l_int32 *)CALLOC(w, sizeof(l_int32));
01013         buf2b = (l_int32 *)CALLOC(w, sizeof(l_int32));
01014         if (!bufu8r || !bufu8g || !bufu8b)
01015             return (PIX *)ERROR_PTR("uint8 line buf not made", procName, NULL);
01016         if (!buf1r || !buf1g || !buf1b || !buf2r || !buf2g || !buf2b)
01017             return (PIX *)ERROR_PTR("mono line buf not made", procName, NULL);
01018 
01019             /* Start by priming buf2; line 1 is above line 2 */
01020         pixGetRGBLine(pixs, 0, bufu8r, bufu8g, bufu8b);
01021         for (j = 0; j < w; j++) {
01022             buf2r[j] = 64 * bufu8r[j];
01023             buf2g[j] = 64 * bufu8g[j];
01024             buf2b[j] = 64 * bufu8b[j];
01025         }
01026 
01027         for (i = 0; i < h - 1; i++) {
01028                 /* Swap data 2 --> 1, and read in new line 2 */
01029             memcpy(buf1r, buf2r, 4 * w);
01030             memcpy(buf1g, buf2g, 4 * w);
01031             memcpy(buf1b, buf2b, 4 * w);
01032             pixGetRGBLine(pixs, i + 1, bufu8r, bufu8g, bufu8b);
01033             for (j = 0; j < w; j++) {
01034                 buf2r[j] = 64 * bufu8r[j];
01035                 buf2g[j] = 64 * bufu8g[j];
01036                 buf2b[j] = 64 * bufu8b[j];
01037             }
01038 
01039                 /* Dither */
01040             lined = datad + i * wpld;
01041             for (j = 0; j < w - 1; j++) {
01042                 rval = buf1r[j] / 64;
01043                 gval = buf1g[j] / 64;
01044                 bval = buf1b[j] / 64;
01045                 index = ((rval >> rshift) << (2 * sigbits)) +
01046                         ((gval >> rshift) << sigbits) + (bval >> rshift);
01047                 cmapindex = indexmap[index];
01048                 SET_DATA_BYTE(lined, j, cmapindex);
01049                 pixcmapGetColor(cmap, cmapindex, &rc, &gc, &bc);
01050 
01051                 dif = buf1r[j] / 8 - 8 * rc;
01052                 if (dif > DIF_CAP) dif = DIF_CAP;
01053                 if (dif < -DIF_CAP) dif = -DIF_CAP;
01054                 if (dif != 0) {
01055                     val1 = buf1r[j + 1] + 3 * dif;
01056                     val2 = buf2r[j] + 3 * dif;
01057                     val3 = buf2r[j + 1] + 2 * dif;
01058                     if (dif > 0) {
01059                         buf1r[j + 1] = L_MIN(16383, val1);
01060                         buf2r[j] = L_MIN(16383, val2);
01061                         buf2r[j + 1] = L_MIN(16383, val3);
01062                     }
01063                     else if (dif < 0) {
01064                         buf1r[j + 1] = L_MAX(0, val1);
01065                         buf2r[j] = L_MAX(0, val2);
01066                         buf2r[j + 1] = L_MAX(0, val3);
01067                     }
01068                 }
01069 
01070                 dif = buf1g[j] / 8 - 8 * gc;
01071                 if (dif > DIF_CAP) dif = DIF_CAP;
01072                 if (dif < -DIF_CAP) dif = -DIF_CAP;
01073                 if (dif != 0) {
01074                     val1 = buf1g[j + 1] + 3 * dif;
01075                     val2 = buf2g[j] + 3 * dif;
01076                     val3 = buf2g[j + 1] + 2 * dif;
01077                     if (dif > 0) {
01078                         buf1g[j + 1] = L_MIN(16383, val1);
01079                         buf2g[j] = L_MIN(16383, val2);
01080                         buf2g[j + 1] = L_MIN(16383, val3);
01081                     }
01082                     else if (dif < 0) {
01083                         buf1g[j + 1] = L_MAX(0, val1);
01084                         buf2g[j] = L_MAX(0, val2);
01085                         buf2g[j + 1] = L_MAX(0, val3);
01086                     }
01087                 }
01088 
01089                 dif = buf1b[j] / 8 - 8 * bc;
01090                 if (dif > DIF_CAP) dif = DIF_CAP;
01091                 if (dif < -DIF_CAP) dif = -DIF_CAP;
01092                 if (dif != 0) {
01093                     val1 = buf1b[j + 1] + 3 * dif;
01094                     val2 = buf2b[j] + 3 * dif;
01095                     val3 = buf2b[j + 1] + 2 * dif;
01096                     if (dif > 0) {
01097                         buf1b[j + 1] = L_MIN(16383, val1);
01098                         buf2b[j] = L_MIN(16383, val2);
01099                         buf2b[j + 1] = L_MIN(16383, val3);
01100                     }
01101                     else if (dif < 0) {
01102                         buf1b[j + 1] = L_MAX(0, val1);
01103                         buf2b[j] = L_MAX(0, val2);
01104                         buf2b[j + 1] = L_MAX(0, val3);
01105                     }
01106                 }
01107             }
01108 
01109                 /* Get last pixel in row; no downward propagation */
01110             rval = buf1r[w - 1] / 64;
01111             gval = buf1g[w - 1] / 64;
01112             bval = buf1b[w - 1] / 64;
01113             index = ((rval >> rshift) << (2 * sigbits)) +
01114                     ((gval >> rshift) << sigbits) + (bval >> rshift);
01115             SET_DATA_BYTE(lined, w - 1, indexmap[index]);
01116         }
01117 
01118             /* Get last row of pixels; no leftward propagation */
01119         lined = datad + (h - 1) * wpld;
01120         for (j = 0; j < w; j++) {
01121             rval = buf2r[j] / 64;
01122             gval = buf2g[j] / 64;
01123             bval = buf2b[j] / 64;
01124             index = ((rval >> rshift) << (2 * sigbits)) +
01125                     ((gval >> rshift) << sigbits) + (bval >> rshift);
01126             SET_DATA_BYTE(lined, j, indexmap[index]);
01127         }
01128 
01129         FREE(bufu8r);
01130         FREE(bufu8g);
01131         FREE(bufu8b);
01132         FREE(buf1r);
01133         FREE(buf1g);
01134         FREE(buf1b);
01135         FREE(buf2r);
01136         FREE(buf2g);
01137         FREE(buf2b);
01138     }
01139 
01140     return pixd;
01141 }
01142 
01143 
01144 /*!
01145  *  getColorIndexMedianCut()
01146  *
01147  *      Input:  pixel (32 bit rgb)
01148  *              rshift (of component: 8 - sigbits)
01149  *              mask (over sigbits)
01150  *              sigbits
01151  *              &index (<return> rgb index value)
01152  *      Return: void
01153  *
01154  *  Notes:
01155  *      (1) This is used on each pixel in the source image.  No checking
01156  *          is done on input values.
01157  */
01158 static void
01159 getColorIndexMedianCut(l_uint32  pixel,
01160                        l_int32   rshift,
01161                        l_uint32  mask,
01162                        l_int32   sigbits,
01163                        l_int32  *pindex)
01164 {
01165 l_int32  rval, gval, bval;
01166 
01167     rval = pixel >> (24 + rshift);
01168     gval = (pixel >> (16 + rshift)) & mask;
01169     bval = (pixel >> (8 + rshift)) & mask;
01170     *pindex = (rval << (2 * sigbits)) + (gval << sigbits) + bval;
01171     return;
01172 }
01173 
01174 
01175 /*!
01176  *  pixGetColorRegion()
01177  *
01178  *      Input:  pixs  (32 bpp; rgb color)
01179  *              sigbits (valid: 5, 6)
01180  *              subsample (integer > 0)
01181  *      Return: vbox (minimum 3D box in color space enclosing all pixels),
01182  *              or null on error
01183  *
01184  *  Notes:
01185  *      (1) Computes the minimum 3D box in color space enclosing all
01186  *          pixels in the image.
01187  */
01188 static L_BOX3D *
01189 pixGetColorRegion(PIX     *pixs,
01190                   l_int32  sigbits,
01191                   l_int32  subsample)
01192 {
01193 l_int32    rmin, rmax, gmin, gmax, bmin, bmax, rval, gval, bval;
01194 l_int32    w, h, wpl, i, j, rshift;
01195 l_uint32   mask, pixel;
01196 l_uint32  *data, *line;
01197 
01198     PROCNAME("pixGetColorRegion");
01199 
01200     if (!pixs)
01201         return (L_BOX3D *)ERROR_PTR("pixs not defined", procName, NULL);
01202 
01203     rmin = gmin = bmin = 1000000;
01204     rmax = gmax = bmax = 0;
01205     rshift = 8 - sigbits;
01206     mask = 0xff >> rshift;
01207     pixGetDimensions(pixs, &w, &h, NULL);
01208     data = pixGetData(pixs);
01209     wpl = pixGetWpl(pixs);
01210     for (i = 0; i < h; i += subsample) {
01211         line = data + i * wpl;
01212         for (j = 0; j < w; j += subsample) {
01213             pixel = line[j];
01214             rval = pixel >> (24 + rshift);
01215             gval = (pixel >> (16 + rshift)) & mask;
01216             bval = (pixel >> (8 + rshift)) & mask;
01217             if (rval < rmin)
01218                 rmin = rval;
01219             else if (rval > rmax)
01220                 rmax = rval;
01221             if (gval < gmin)
01222                 gmin = gval;
01223             else if (gval > gmax)
01224                 gmax = gval;
01225             if (bval < bmin)
01226                 bmin = bval;
01227             else if (bval > bmax)
01228                 bmax = bval;
01229         }
01230     }
01231 
01232     return box3dCreate(rmin, rmax, gmin, gmax, bmin, bmax);
01233 }
01234 
01235 
01236 /*!
01237  *  medianCutApply()
01238  *
01239  *      Input:  histo  (array; in rgb colorspace)
01240  *              sigbits
01241  *              vbox (input 3D box)
01242  *              &vbox1, vbox2 (<return> vbox split in two parts)
01243  *      Return: 0 if OK, 1 on error
01244  */
01245 static l_int32
01246 medianCutApply(l_int32   *histo,
01247                l_int32    sigbits,
01248                L_BOX3D   *vbox,
01249                L_BOX3D  **pvbox1,
01250                L_BOX3D  **pvbox2)
01251 {
01252 l_int32   i, j, k, sum, rw, gw, bw, maxw, index;
01253 l_int32   total, left, right;
01254 l_int32   partialsum[128];
01255 L_BOX3D  *vbox1, *vbox2;
01256 
01257     PROCNAME("medianCutApply");
01258 
01259     if (!histo) 
01260         return ERROR_INT("histo not defined", procName, 1);
01261     if (!vbox) 
01262         return ERROR_INT("vbox not defined", procName, 1);
01263     if (!pvbox1 || !pvbox2) 
01264         return ERROR_INT("&vbox1 and &vbox2 not both defined", procName, 1);
01265 
01266     *pvbox1 = *pvbox2 = NULL;
01267     if (vboxGetCount(vbox, histo, sigbits) == 0)
01268         return ERROR_INT("no pixels in vbox", procName, 1);
01269 
01270         /* If the vbox occupies just one element in color space, it can't
01271          * be split.  Leave the 'sortparam' field at 0, so that it goes to
01272          * the tail of the priority queue and stays there, thereby avoiding
01273          * an infinite loop (take off, put back on the head) if it
01274          * happens to be the most populous box! */
01275     rw = vbox->r2 - vbox->r1 + 1;
01276     gw = vbox->g2 - vbox->g1 + 1;
01277     bw = vbox->b2 - vbox->b1 + 1;
01278     if (rw == 1 && gw == 1 && bw == 1) {
01279         *pvbox1 = box3dCopy(vbox);
01280         return 0;
01281     }
01282 
01283         /* Select the longest axis for splitting */
01284     maxw = L_MAX(rw, gw);
01285     maxw = L_MAX(maxw, bw);
01286 #if  DEBUG_SPLIT_AXES
01287     if (rw == maxw)
01288         fprintf(stderr, "red split\n");
01289     else if (gw == maxw)
01290         fprintf(stderr, "green split\n");
01291     else
01292         fprintf(stderr, "blue split\n");
01293 #endif  /* DEBUG_SPLIT_AXES */
01294 
01295         /* Find the partial sum arrays along the selected axis. */
01296     total = 0;
01297     if (maxw == rw) {
01298         for (i = vbox->r1; i <= vbox->r2; i++) {
01299             sum = 0;
01300             for (j = vbox->g1; j <= vbox->g2; j++) {
01301                 for (k = vbox->b1; k <= vbox->b2; k++) {
01302                     index = (i << (2 * sigbits)) + (j << sigbits) + k;
01303                     sum += histo[index];
01304                 }
01305             }
01306             total += sum;
01307             partialsum[i] = total;
01308         }
01309     }
01310     else if (maxw == gw) {
01311         for (i = vbox->g1; i <= vbox->g2; i++) {
01312             sum = 0;
01313             for (j = vbox->r1; j <= vbox->r2; j++) {
01314                 for (k = vbox->b1; k <= vbox->b2; k++) {
01315                     index = (i << sigbits) + (j << (2 * sigbits)) + k;
01316                     sum += histo[index];
01317                 }
01318             }
01319             total += sum;
01320             partialsum[i] = total;
01321         }
01322     }
01323     else {  /* maxw == bw */
01324         for (i = vbox->b1; i <= vbox->b2; i++) {
01325             sum = 0;
01326             for (j = vbox->r1; j <= vbox->r2; j++) {
01327                 for (k = vbox->g1; k <= vbox->g2; k++) {
01328                     index = i + (j << (2 * sigbits)) + (k << sigbits);
01329                     sum += histo[index];
01330                 }
01331             }
01332             total += sum;
01333             partialsum[i] = total;
01334         }
01335     }
01336 
01337         /* Determine the cut planes, making sure that two vboxes
01338          * are always produced.  Generate the two vboxes and compute
01339          * the sum in each of them.  Choose the cut plane within
01340          * the greater of the (left, right) sides of the bin in which
01341          * the median pixel resides.  Here's the surprise: go halfway
01342          * into that side.  By doing that, you technically move away
01343          * from "median cut," but in the process a significant number
01344          * of low-count vboxes are produced, allowing much better
01345          * reproduction of low-count spot colors. */
01346     if (maxw == rw) {
01347         for (i = vbox->r1; i <= vbox->r2; i++) {
01348             if (partialsum[i] > total / 2) {
01349                 vbox1 = box3dCopy(vbox);
01350                 vbox2 = box3dCopy(vbox);
01351                 left = i - vbox->r1;
01352                 right = vbox->r2 - i;
01353                 if (left <= right)
01354                     vbox1->r2 = L_MIN(vbox->r2 - 1, i + right / 2);
01355                 else  /* left > right */
01356                     vbox1->r2 = L_MAX(vbox->r1, i - 1 - left / 2);
01357                 vbox2->r1 = vbox1->r2 + 1;
01358                 break;
01359             }
01360         }
01361     }
01362     else if (maxw == gw) {
01363         for (i = vbox->g1; i <= vbox->g2; i++) {
01364             if (partialsum[i] > total / 2) {
01365                 vbox1 = box3dCopy(vbox);
01366                 vbox2 = box3dCopy(vbox);
01367                 left = i - vbox->g1;
01368                 right = vbox->g2 - i;
01369                 if (left <= right)
01370                     vbox1->g2 = L_MIN(vbox->g2 - 1, i + right / 2);
01371                 else  /* left > right */
01372                     vbox1->g2 = L_MAX(vbox->g1, i - 1 - left / 2);
01373                 vbox2->g1 = vbox1->g2 + 1;
01374                 break;
01375             }
01376         }
01377     }
01378     else {  /* maxw == bw */
01379         for (i = vbox->b1; i <= vbox->b2; i++) {
01380             if (partialsum[i] > total / 2) {
01381                 vbox1 = box3dCopy(vbox);
01382                 vbox2 = box3dCopy(vbox);
01383                 left = i - vbox->b1;
01384                 right = vbox->b2 - i;
01385                 if (left <= right)
01386                     vbox1->b2 = L_MIN(vbox->b2 - 1, i + right / 2);
01387                 else  /* left > right */
01388                     vbox1->b2 = L_MAX(vbox->b1, i - 1 - left / 2);
01389                 vbox2->b1 = vbox1->b2 + 1;
01390                 break;
01391             }
01392         }
01393     }
01394     vbox1->npix = vboxGetCount(vbox1, histo, sigbits);
01395     vbox2->npix = vboxGetCount(vbox2, histo, sigbits);
01396     vbox1->vol = vboxGetVolume(vbox1);
01397     vbox2->vol = vboxGetVolume(vbox2);
01398     *pvbox1 = vbox1;
01399     *pvbox2 = vbox2;
01400 
01401     return 0;
01402 }
01403 
01404 
01405 /*!
01406  *  pixcmapGenerateFromMedianCuts()
01407  *
01408  *      Input:  lh (priority queue of pointers to vboxes)
01409  *              histo
01410  *              sigbits (valid: 5 or 6)
01411  *      Return: cmap, or null on error
01412  *
01413  *  Notes:
01414  *      (1) Each vbox in the heap represents a color in the colormap.
01415  *      (2) As a side-effect, the histo becomes an inverse colormap,
01416  *          where the part of the array correpsonding to each vbox
01417  *          is labeled with the cmap index for that vbox.  Then
01418  *          for each rgb pixel, the colormap index is found directly
01419  *          by mapping the rgb value to the histo array index.
01420  */
01421 static PIXCMAP *
01422 pixcmapGenerateFromMedianCuts(L_HEAP   *lh,
01423                               l_int32  *histo,
01424                               l_int32   sigbits)
01425 {
01426 l_int32   index, rval, gval, bval;
01427 L_BOX3D  *vbox;
01428 PIXCMAP  *cmap;
01429 
01430     PROCNAME("pixcmapGenerateFromMedianCuts");
01431 
01432     if (!lh) 
01433         return (PIXCMAP *)ERROR_PTR("lh not defined", procName, NULL);
01434     if (!histo) 
01435         return (PIXCMAP *)ERROR_PTR("histo not defined", procName, NULL);
01436 
01437     rval = gval = bval = 0;  /* make compiler happy */
01438     cmap = pixcmapCreate(8);
01439     index = 0;
01440     while (lheapGetCount(lh) > 0) {
01441         vbox = (L_BOX3D *)lheapRemove(lh);
01442         vboxGetAverageColor(vbox, histo, sigbits, index, &rval, &gval, &bval);
01443         pixcmapAddColor(cmap, rval, gval, bval);
01444         FREE(vbox);
01445         index++;
01446     }
01447 
01448     return cmap;
01449 }
01450 
01451 
01452 /*!
01453  *  vboxGetAverageColor()
01454  *
01455  *      Input:  vbox (3d region of color space for one quantized color)
01456  *              histo
01457  *              sigbits (valid: 5 or 6)
01458  *              index (if >= 0, assign to all colors in histo in this vbox)
01459  *              &rval, &gval, &bval (<returned> average color)
01460  *      Return: cmap, or null on error
01461  *
01462  *  Notes:
01463  *      (1) The vbox represents one color in the colormap.
01464  *      (2) If index >= 0, as a side-effect, all array elements in
01465  *          the histo corresponding to the vbox are labeled with this
01466  *          cmap index for that vbox.  Otherwise, the histo array
01467  *          is not changed.
01468  */
01469 static l_int32
01470 vboxGetAverageColor(L_BOX3D  *vbox,
01471                     l_int32  *histo,
01472                     l_int32   sigbits,
01473                     l_int32   index,
01474                     l_int32  *prval,
01475                     l_int32  *pgval,
01476                     l_int32  *pbval)
01477 {
01478 l_int32  i, j, k, ntot, mult, histoindex, rsum, gsum, bsum;
01479 
01480     PROCNAME("vboxGetAverageColor");
01481 
01482     if (!vbox) 
01483         return ERROR_INT("vbox not defined", procName, 1);
01484     if (!histo) 
01485         return ERROR_INT("histo not defined", procName, 1);
01486     if (!prval || !pgval || !pbval)
01487         return ERROR_INT("&p*val not all defined", procName, 1);
01488 
01489     *prval = *pgval = *pbval = 0;
01490     ntot = 0;
01491     mult = 1 << (8 - sigbits);
01492     rsum = gsum = bsum = 0;
01493     for (i = vbox->r1; i <= vbox->r2; i++) {
01494         for (j = vbox->g1; j <= vbox->g2; j++) {
01495             for (k = vbox->b1; k <= vbox->b2; k++) {
01496                  histoindex = (i << (2 * sigbits)) + (j << sigbits) + k;
01497                  ntot += histo[histoindex];
01498                  rsum += (l_int32)(histo[histoindex] * (i + 0.5) * mult);
01499                  gsum += (l_int32)(histo[histoindex] * (j + 0.5) * mult);
01500                  bsum += (l_int32)(histo[histoindex] * (k + 0.5) * mult);
01501                  if (index >= 0)
01502                      histo[histoindex] = index;
01503             }
01504         }
01505     }
01506 
01507     if (ntot == 0) {
01508         *prval = mult * (vbox->r1 + vbox->r2 + 1) / 2;
01509         *pgval = mult * (vbox->g1 + vbox->g2 + 1) / 2;
01510         *pbval = mult * (vbox->b1 + vbox->b2 + 1) / 2;
01511     }
01512     else {
01513         *prval = rsum / ntot;
01514         *pgval = gsum / ntot;
01515         *pbval = bsum / ntot;
01516     }
01517 
01518 #if  DEBUG_MC_COLORS
01519     fprintf(stderr, "ntot[%d] = %d: [%d, %d, %d], (%d, %d, %d)\n",
01520             index, ntot, vbox->r2 - vbox->r1 + 1,
01521             vbox->g2 - vbox->g1 + 1, vbox->b2 - vbox->b1 + 1,
01522             *prval, *pgval, *pbval);
01523 #endif  /* DEBUG_MC_COLORS */
01524 
01525     return 0;
01526 }
01527 
01528 
01529 /*!
01530  *  vboxGetCount()
01531  *
01532  *      Input:  vbox (3d region of color space for one quantized color)
01533  *              histo
01534  *              sigbits (valid: 5 or 6)
01535  *      Return: number of image pixels in this region, or 0 on error
01536  */
01537 static l_int32
01538 vboxGetCount(L_BOX3D  *vbox,
01539              l_int32  *histo,
01540              l_int32   sigbits)
01541 {
01542 l_int32  i, j, k, npix, index;
01543 
01544     PROCNAME("vboxGetCount");
01545 
01546     if (!vbox) 
01547         return ERROR_INT("vbox not defined", procName, 0);
01548     if (!histo) 
01549         return ERROR_INT("histo not defined", procName, 0);
01550 
01551     npix = 0;
01552     for (i = vbox->r1; i <= vbox->r2; i++) {
01553         for (j = vbox->g1; j <= vbox->g2; j++) {
01554             for (k = vbox->b1; k <= vbox->b2; k++) {
01555                  index = (i << (2 * sigbits)) + (j << sigbits) + k;
01556                  npix += histo[index];
01557             }
01558         }
01559     }
01560 
01561     return npix;
01562 }
01563 
01564 
01565 /*!
01566  *  vboxGetVolume()
01567  *
01568  *      Input:  vbox (3d region of color space for one quantized color)
01569  *      Return: quantized volume of vbox, or 0 on error
01570  */
01571 static l_int32
01572 vboxGetVolume(L_BOX3D  *vbox)
01573 {
01574     PROCNAME("vboxGetVolume");
01575 
01576     if (!vbox) 
01577         return ERROR_INT("vbox not defined", procName, 0);
01578 
01579     return ((vbox->r2 - vbox->r1 + 1) * (vbox->g2 - vbox->g1 + 1) *
01580             (vbox->b2 - vbox->b1 + 1));
01581 }
01582 
01583 
01584 static L_BOX3D *
01585 box3dCreate(l_int32  r1,
01586             l_int32  r2,
01587             l_int32  g1,
01588             l_int32  g2,
01589             l_int32  b1,
01590             l_int32  b2)
01591 {
01592 L_BOX3D  *vbox;
01593 
01594     vbox = (L_BOX3D *)CALLOC(1, sizeof(L_BOX3D));
01595     vbox->r1 = r1;
01596     vbox->r2 = r2;
01597     vbox->g1 = g1;
01598     vbox->g2 = g2;
01599     vbox->b1 = b1;
01600     vbox->b2 = b2;
01601     return vbox;
01602 }
01603 
01604 
01605 /*
01606  *  Note: don't copy the sortparam.
01607  */
01608 static L_BOX3D *
01609 box3dCopy(L_BOX3D  *vbox)
01610 {
01611 L_BOX3D  *vboxc;
01612 
01613     PROCNAME("box3dCopy");
01614 
01615     if (!vbox) 
01616         return (L_BOX3D *)ERROR_PTR("vbox not defined", procName, NULL);
01617 
01618     vboxc = box3dCreate(vbox->r1, vbox->r2, vbox->g1, vbox->g2,
01619                         vbox->b1, vbox->b2);
01620     vboxc->npix = vbox->npix;
01621     vboxc->vol = vbox->vol;
01622     return vboxc;
01623 }
01624     
01625 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines