Leptonica 1.68
C Image Processing Library
|
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