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 * colorseg.c 00018 * 00019 * Unsupervised color segmentation 00020 * 00021 * PIX *pixColorSegment() 00022 * PIX *pixColorSegmentCluster() 00023 * static l_int32 pixColorSegmentTryCluster() 00024 * l_int32 pixAssignToNearestColor() 00025 * l_int32 pixColorSegmentClean() 00026 * l_int32 pixColorSegmentRemoveColors() 00027 */ 00028 00029 #include "allheaders.h" 00030 00031 /* Maximum allowed iterations in Phase 1. */ 00032 static const l_int32 MAX_ALLOWED_ITERATIONS = 20; 00033 00034 /* Factor by which max dist is increased on each iteration */ 00035 static const l_float32 DIST_EXPAND_FACT = 1.3; 00036 00037 /* Octcube division level for computing nearest colormap color using LUT. 00038 * Using 4 should suffice for up to 50 - 100 colors, and it is 00039 * very fast. Using 5 takes 8 times as long to set up the LUT 00040 * for little perceptual gain, even with 100 colors. */ 00041 static const l_int32 LEVEL_IN_OCTCUBE = 4; 00042 00043 00044 static l_int32 pixColorSegmentTryCluster(PIX *pixd, PIX *pixs, 00045 l_int32 maxdist, l_int32 maxcolors); 00046 00047 00048 /*------------------------------------------------------------------* 00049 * Unsupervised color segmentation * 00050 *------------------------------------------------------------------*/ 00051 /*! 00052 * pixColorSegment() 00053 * 00054 * Input: pixs (32 bpp; 24-bit color) 00055 * maxdist (max euclidean dist to existing cluster) 00056 * maxcolors (max number of colors allowed in first pass) 00057 * selsize (linear size of sel for closing to remove noise) 00058 * finalcolors (max number of final colors allowed after 4th pass) 00059 * Return: pixd (8 bit with colormap), or null on error 00060 * 00061 * Color segmentation proceeds in four phases: 00062 * 00063 * Phase 1: pixColorSegmentCluster() 00064 * The image is traversed in raster order. Each pixel either 00065 * becomes the representative for a new cluster or is assigned to an 00066 * existing cluster. Assignment is greedy. The data is stored in 00067 * a colormapped image. Three auxiliary arrays are used to hold 00068 * the colors of the representative pixels, for fast lookup. 00069 * The average color in each cluster is computed. 00070 * 00071 * Phase 2. pixAssignToNearestColor() 00072 * A second (non-greedy) clustering pass is performed, where each pixel 00073 * is assigned to the nearest cluster (average). We also keep track 00074 * of how many pixels are assigned to each cluster. 00075 * 00076 * Phase 3. pixColorSegmentClean() 00077 * For each cluster, starting with the largest, do a morphological 00078 * closing to eliminate small components within larger ones. 00079 * 00080 * Phase 4. pixColorSegmentRemoveColors() 00081 * Eliminate all colors except the most populated 'finalcolors'. 00082 * Then remove unused colors from the colormap, and reassign those 00083 * pixels to the nearest remaining cluster, using the original pixel values. 00084 * 00085 * Notes: 00086 * (1) The goal is to generate a small number of colors. 00087 * Typically this would be specified by 'finalcolors', 00088 * a number that would be somewhere between 3 and 6. 00089 * The parameter 'maxcolors' specifies the maximum number of 00090 * colors generated in the first phase. This should be 00091 * larger than finalcolors, perhaps twice as large. 00092 * If more than 'maxcolors' are generated in the first phase 00093 * using the input 'maxdist', the distance is repeatedly 00094 * increased by a multiplicative factor until the condition 00095 * is satisfied. The implicit relation between 'maxdist' 00096 * and 'maxcolors' is thus adjusted programmatically. 00097 * (2) As a very rough guideline, given a target value of 'finalcolors', 00098 * here are approximate values of 'maxdist' and 'maxcolors' 00099 * to start with: 00100 * 00101 * finalcolors maxcolors maxdist 00102 * ----------- --------- ------- 00103 * 3 6 100 00104 * 4 8 90 00105 * 5 10 75 00106 * 6 12 60 00107 * 00108 * For a given number of finalcolors, if you use too many 00109 * maxcolors, the result will be noisy. If you use too few, 00110 * the result will be a relatively poor assignment of colors. 00111 */ 00112 PIX * 00113 pixColorSegment(PIX *pixs, 00114 l_int32 maxdist, 00115 l_int32 maxcolors, 00116 l_int32 selsize, 00117 l_int32 finalcolors) 00118 { 00119 l_int32 *countarray; 00120 PIX *pixd; 00121 00122 PROCNAME("pixColorSegment"); 00123 00124 if (!pixs) 00125 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00126 if (pixGetDepth(pixs) != 32) 00127 return (PIX *)ERROR_PTR("must be rgb color", procName, NULL); 00128 00129 /* Phase 1; original segmentation */ 00130 if ((pixd = pixColorSegmentCluster(pixs, maxdist, maxcolors)) == NULL) 00131 return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL); 00132 /* pixWrite("junkpixd1", pixd, IFF_PNG); */ 00133 00134 /* Phase 2; refinement in pixel assignment */ 00135 if ((countarray = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL) 00136 return (PIX *)ERROR_PTR("countarray not made", procName, NULL); 00137 pixAssignToNearestColor(pixd, pixs, NULL, LEVEL_IN_OCTCUBE, countarray); 00138 /* pixWrite("junkpixd2", pixd, IFF_PNG); */ 00139 00140 /* Phase 3: noise removal by separately closing each color */ 00141 pixColorSegmentClean(pixd, selsize, countarray); 00142 /* pixWrite("junkpixd3", pixd, IFF_PNG); */ 00143 FREE(countarray); 00144 00145 /* Phase 4: removal of colors with small population and 00146 * reassignment of pixels to remaining colors */ 00147 pixColorSegmentRemoveColors(pixd, pixs, finalcolors); 00148 return pixd; 00149 } 00150 00151 00152 /*! 00153 * pixColorSegmentCluster() 00154 * 00155 * Input: pixs (32 bpp; 24-bit color) 00156 * maxdist (max euclidean dist to existing cluster) 00157 * maxcolors (max number of colors allowed in first pass) 00158 * Return: pixd (8 bit with colormap), or null on error 00159 * 00160 * Notes: 00161 * (1) This is phase 1. See description in pixColorSegment(). 00162 * (2) Greedy unsupervised classification. If the limit 'maxcolors' 00163 * is exceeded, the computation is repeated with a larger 00164 * allowed cluster size. 00165 * (3) On each successive iteration, 'maxdist' is increased by a 00166 * constant factor. See comments in pixColorSegment() for 00167 * a guideline on parameter selection. 00168 * Note that the diagonal of the 8-bit rgb color cube is about 00169 * 440, so for 'maxdist' = 440, you are guaranteed to get 1 color! 00170 */ 00171 PIX * 00172 pixColorSegmentCluster(PIX *pixs, 00173 l_int32 maxdist, 00174 l_int32 maxcolors) 00175 { 00176 l_int32 w, h, newmaxdist, ret, niters, ncolors, success; 00177 PIX *pixd; 00178 PIXCMAP *cmap; 00179 00180 PROCNAME("pixColorSegmentCluster"); 00181 00182 if (!pixs) 00183 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00184 if (pixGetDepth(pixs) != 32) 00185 return (PIX *)ERROR_PTR("must be rgb color", procName, NULL); 00186 00187 w = pixGetWidth(pixs); 00188 h = pixGetHeight(pixs); 00189 if ((pixd = pixCreate(w, h, 8)) == NULL) 00190 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00191 cmap = pixcmapCreate(8); 00192 pixSetColormap(pixd, cmap); 00193 pixCopyResolution(pixd, pixs); 00194 00195 newmaxdist = maxdist; 00196 niters = 0; 00197 success = TRUE; 00198 while (1) { 00199 ret = pixColorSegmentTryCluster(pixd, pixs, newmaxdist, maxcolors); 00200 niters++; 00201 if (!ret) { 00202 ncolors = pixcmapGetCount(cmap); 00203 L_INFO_INT2("Success with %d colors after %d iters", procName, 00204 ncolors, niters); 00205 break; 00206 } 00207 if (niters == MAX_ALLOWED_ITERATIONS) { 00208 L_WARNING_INT("too many iters; newmaxdist = %d", 00209 procName, newmaxdist); 00210 success = FALSE; 00211 break; 00212 } 00213 newmaxdist = (l_int32)(DIST_EXPAND_FACT * (l_float32)newmaxdist); 00214 } 00215 00216 if (!success) { 00217 pixDestroy(&pixd); 00218 return (PIX *)ERROR_PTR("failure in phase 1", procName, NULL); 00219 } 00220 00221 return pixd; 00222 } 00223 00224 00225 /*! 00226 * pixColorSegmentTryCluster() 00227 * 00228 * Input: pixd 00229 * pixs 00230 * maxdist 00231 * maxcolors 00232 * Return: 0 if OK, 1 on error 00233 * 00234 * Note: This function should only be called from pixColorSegCluster() 00235 */ 00236 static l_int32 00237 pixColorSegmentTryCluster(PIX *pixd, 00238 PIX *pixs, 00239 l_int32 maxdist, 00240 l_int32 maxcolors) 00241 { 00242 l_int32 rmap[256], gmap[256], bmap[256]; 00243 l_int32 w, h, wpls, wpld, i, j, k, found, ret, index, ncolors; 00244 l_int32 rval, gval, bval, dist2, maxdist2; 00245 l_int32 countarray[256]; 00246 l_int32 rsum[256], gsum[256], bsum[256]; 00247 l_uint32 *ppixel; 00248 l_uint32 *datas, *datad, *lines, *lined; 00249 PIXCMAP *cmap; 00250 00251 PROCNAME("pixColorSegmentTryCluster"); 00252 00253 if (!pixs) 00254 return ERROR_INT("pixs not defined", procName, 1); 00255 if (!pixd) 00256 return ERROR_INT("pixd not defined", procName, 1); 00257 00258 w = pixGetWidth(pixs); 00259 h = pixGetHeight(pixs); 00260 maxdist2 = maxdist * maxdist; 00261 cmap = pixGetColormap(pixd); 00262 pixcmapClear(cmap); 00263 for (k = 0; k < 256; k++) { 00264 rsum[k] = gsum[k] = bsum[k] = 0; 00265 rmap[k] = gmap[k] = bmap[k] = 0; 00266 } 00267 00268 datas = pixGetData(pixs); 00269 datad = pixGetData(pixd); 00270 wpls = pixGetWpl(pixs); 00271 wpld = pixGetWpl(pixd); 00272 for (i = 0; i < h; i++) { 00273 lines = datas + i * wpls; 00274 lined = datad + i * wpld; 00275 for (j = 0; j < w; j++) { 00276 ppixel = lines + j; 00277 rval = GET_DATA_BYTE(ppixel, COLOR_RED); 00278 gval = GET_DATA_BYTE(ppixel, COLOR_GREEN); 00279 bval = GET_DATA_BYTE(ppixel, COLOR_BLUE); 00280 ncolors = pixcmapGetCount(cmap); 00281 found = FALSE; 00282 for (k = 0; k < ncolors; k++) { 00283 dist2 = (rval - rmap[k]) * (rval - rmap[k]) + 00284 (gval - gmap[k]) * (gval - gmap[k]) + 00285 (bval - bmap[k]) * (bval - bmap[k]); 00286 if (dist2 <= maxdist2) { /* take it; greedy */ 00287 found = TRUE; 00288 SET_DATA_BYTE(lined, j, k); 00289 countarray[k]++; 00290 rsum[k] += rval; 00291 gsum[k] += gval; 00292 bsum[k] += bval; 00293 break; 00294 } 00295 } 00296 if (!found) { /* Add a new color */ 00297 ret = pixcmapAddNewColor(cmap, rval, gval, bval, &index); 00298 /* fprintf(stderr, 00299 "index = %d, (i,j) = (%d,%d), rgb = (%d, %d, %d)\n", 00300 index, i, j, rval, gval, bval); */ 00301 if (ret == 0 && index < maxcolors) { 00302 countarray[index] = 1; 00303 SET_DATA_BYTE(lined, j, index); 00304 rmap[index] = rval; 00305 gmap[index] = gval; 00306 bmap[index] = bval; 00307 rsum[index] = rval; 00308 gsum[index] = gval; 00309 bsum[index] = bval; 00310 } 00311 else { 00312 L_INFO_INT("maxcolors exceeded for maxdist = %d", 00313 procName, maxdist); 00314 return 1; 00315 } 00316 } 00317 } 00318 } 00319 00320 /* Replace the colors in the colormap by the averages */ 00321 for (k = 0; k < ncolors; k++) { 00322 rval = rsum[k] / countarray[k]; 00323 gval = gsum[k] / countarray[k]; 00324 bval = bsum[k] / countarray[k]; 00325 pixcmapResetColor(cmap, k, rval, gval, bval); 00326 } 00327 00328 return 0; 00329 } 00330 00331 00332 /*! 00333 * pixAssignToNearestColor() 00334 * 00335 * Input: pixd (8 bpp, colormapped) 00336 * pixs (32 bpp; 24-bit color) 00337 * pixm (<optional> 1 bpp) 00338 * level (of octcube used for finding nearest color in cmap) 00339 * countarray (<optional> ptr to array, in which we can store 00340 * the number of pixels found in each color in 00341 * the colormap in pixd) 00342 * Return: 0 if OK, 1 on error 00343 * 00344 * Notes: 00345 * (1) This is used in phase 2 of color segmentation, where pixs 00346 * is the original input image to pixColorSegment(), and 00347 * pixd is the colormapped image returned from 00348 * pixColorSegmentCluster(). It is also used, with a mask, 00349 * in phase 4. 00350 * (2) This is an in-place operation. 00351 * (3) The colormap in pixd is unchanged. 00352 * (4) pixs and pixd must be the same size (w, h). 00353 * (5) The selection mask pixm can be null. If it exists, it must 00354 * be the same size as pixs and pixd, and only pixels 00355 * corresponding to fg in pixm are assigned. Set to 00356 * NULL if all pixels in pixd are to be assigned. 00357 * (6) The countarray can be null. If it exists, it is pre-allocated 00358 * and of a size at least equal to the size of the colormap in pixd. 00359 * (7) This does a best-fit (non-greedy) assignment of pixels to 00360 * existing clusters. Specifically, it assigns each pixel 00361 * in pixd to the color index in the pixd colormap that has a 00362 * color closest to the corresponding rgb pixel in pixs. 00363 * (8) 'level' is the octcube level used to quickly find the nearest 00364 * color in the colormap for each pixel. For color segmentation, 00365 * this parameter is set to LEVEL_IN_OCTCUBE. 00366 * (9) We build a mapping table from octcube to colormap index so 00367 * that this function can run in a time (otherwise) independent 00368 * of the number of colors in the colormap. This avoids a 00369 * brute-force search for the closest colormap color to each 00370 * pixel in the image. 00371 */ 00372 l_int32 00373 pixAssignToNearestColor(PIX *pixd, 00374 PIX *pixs, 00375 PIX *pixm, 00376 l_int32 level, 00377 l_int32 *countarray) 00378 { 00379 l_int32 w, h, wpls, wpld, wplm, i, j; 00380 l_int32 rval, gval, bval, index; 00381 l_int32 *cmaptab; 00382 l_uint32 octindex; 00383 l_uint32 *rtab, *gtab, *btab; 00384 l_uint32 *ppixel; 00385 l_uint32 *datas, *datad, *datam, *lines, *lined, *linem; 00386 PIXCMAP *cmap; 00387 00388 PROCNAME("pixAssignToNearestColor"); 00389 00390 if (!pixd) 00391 return ERROR_INT("pixd not defined", procName, 1); 00392 if ((cmap = pixGetColormap(pixd)) == NULL) 00393 return ERROR_INT("cmap not found", procName, 1); 00394 if (!pixs) 00395 return ERROR_INT("pixs not defined", procName, 1); 00396 if (pixGetDepth(pixs) != 32) 00397 return ERROR_INT("pixs not 32 bpp", procName, 1); 00398 00399 /* Set up the tables to map rgb to the nearest colormap index */ 00400 if (makeRGBToIndexTables(&rtab, >ab, &btab, level)) 00401 return ERROR_INT("index tables not made", procName, 1); 00402 if ((cmaptab = pixcmapToOctcubeLUT(cmap, level, L_MANHATTAN_DISTANCE)) 00403 == NULL) 00404 return ERROR_INT("cmaptab not made", procName, 1); 00405 00406 w = pixGetWidth(pixs); 00407 h = pixGetHeight(pixs); 00408 datas = pixGetData(pixs); 00409 datad = pixGetData(pixd); 00410 wpls = pixGetWpl(pixs); 00411 wpld = pixGetWpl(pixd); 00412 if (pixm) { 00413 datam = pixGetData(pixm); 00414 wplm = pixGetWpl(pixm); 00415 } 00416 for (i = 0; i < h; i++) { 00417 lines = datas + i * wpls; 00418 lined = datad + i * wpld; 00419 if (pixm) 00420 linem = datam + i * wplm; 00421 for (j = 0; j < w; j++) { 00422 if (pixm) { 00423 if (!GET_DATA_BIT(linem, j)) 00424 continue; 00425 } 00426 ppixel = lines + j; 00427 rval = GET_DATA_BYTE(ppixel, COLOR_RED); 00428 gval = GET_DATA_BYTE(ppixel, COLOR_GREEN); 00429 bval = GET_DATA_BYTE(ppixel, COLOR_BLUE); 00430 /* Map from rgb to octcube index */ 00431 getOctcubeIndexFromRGB(rval, gval, bval, rtab, gtab, btab, 00432 &octindex); 00433 /* Map from octcube index to nearest colormap index */ 00434 index = cmaptab[octindex]; 00435 if (countarray) 00436 countarray[index]++; 00437 SET_DATA_BYTE(lined, j, index); 00438 } 00439 } 00440 00441 FREE(cmaptab); 00442 FREE(rtab); 00443 FREE(gtab); 00444 FREE(btab); 00445 return 0; 00446 } 00447 00448 00449 /*! 00450 * pixColorSegmentClean() 00451 * 00452 * Input: pixs (8 bpp, colormapped) 00453 * selsize (for closing) 00454 * countarray (ptr to array containing the number of pixels 00455 * found in each color in the colormap) 00456 * Return: 0 if OK, 1 on error 00457 * 00458 * Notes: 00459 * (1) This operation is in-place. 00460 * (2) This is phase 3 of color segmentation. It is the first 00461 * part of a two-step noise removal process. Colors with a 00462 * large population are closed first; this operation absorbs 00463 * small sets of intercolated pixels of a different color. 00464 */ 00465 l_int32 00466 pixColorSegmentClean(PIX *pixs, 00467 l_int32 selsize, 00468 l_int32 *countarray) 00469 { 00470 l_int32 i, ncolors, val; 00471 l_uint32 val32; 00472 NUMA *na, *nasi; 00473 PIX *pixt1, *pixt2; 00474 PIXCMAP *cmap; 00475 00476 PROCNAME("pixColorSegmentClean"); 00477 00478 if (!pixs) 00479 return ERROR_INT("pixs not defined", procName, 1); 00480 if (pixGetDepth(pixs) != 8) 00481 return ERROR_INT("pixs not 8 bpp", procName, 1); 00482 if ((cmap = pixGetColormap(pixs)) == NULL) 00483 return ERROR_INT("cmap not found", procName, 1); 00484 if (!countarray) 00485 return ERROR_INT("countarray not defined", procName, 1); 00486 if (selsize <= 1) 00487 return 0; /* nothing to do */ 00488 00489 /* Sort colormap indices in decreasing order of pixel population */ 00490 ncolors = pixcmapGetCount(cmap); 00491 na = numaCreate(ncolors); 00492 for (i = 0; i < ncolors; i++) 00493 numaAddNumber(na, countarray[i]); 00494 if ((nasi = numaGetSortIndex(na, L_SORT_DECREASING)) == NULL) 00495 return ERROR_INT("nasi not made", procName, 1); 00496 00497 /* For each color, in order of decreasing population, 00498 * do a closing and absorb the added pixels. Note that 00499 * if the closing removes pixels at the border, they'll 00500 * still appear in the xor and will be properly (re)set. */ 00501 for (i = 0; i < ncolors; i++) { 00502 numaGetIValue(nasi, i, &val); 00503 pixt1 = pixGenerateMaskByValue(pixs, val, 1); 00504 pixt2 = pixCloseSafeCompBrick(NULL, pixt1, selsize, selsize); 00505 pixXor(pixt2, pixt2, pixt1); /* pixels to be added to type 'val' */ 00506 pixcmapGetColor32(cmap, val, &val32); 00507 pixSetMasked(pixs, pixt2, val32); /* add them */ 00508 pixDestroy(&pixt1); 00509 pixDestroy(&pixt2); 00510 } 00511 numaDestroy(&na); 00512 numaDestroy(&nasi); 00513 return 0; 00514 } 00515 00516 00517 /*! 00518 * pixColorSegmentRemoveColors() 00519 * 00520 * Input: pixd (8 bpp, colormapped) 00521 * pixs (32 bpp rgb, with initial pixel values) 00522 * finalcolors (max number of colors to retain) 00523 * Return: 0 if OK, 1 on error 00524 * 00525 * Notes: 00526 * (1) This operation is in-place. 00527 * (2) This is phase 4 of color segmentation, and the second part 00528 * of the 2-step noise removal. Only 'finalcolors' different 00529 * colors are retained, with colors with smaller populations 00530 * being replaced by the nearest color of the remaining colors. 00531 * For highest accuracy, for pixels that are being replaced, 00532 * we find the nearest colormap color to the original rgb color. 00533 */ 00534 l_int32 00535 pixColorSegmentRemoveColors(PIX *pixd, 00536 PIX *pixs, 00537 l_int32 finalcolors) 00538 { 00539 l_int32 i, ncolors, index, tempindex; 00540 l_int32 *tab; 00541 l_uint32 tempcolor; 00542 NUMA *na, *nasi; 00543 PIX *pixm; 00544 PIXCMAP *cmap; 00545 00546 PROCNAME("pixColorSegmentRemoveColors"); 00547 00548 if (!pixd) 00549 return ERROR_INT("pixd not defined", procName, 1); 00550 if (pixGetDepth(pixd) != 8) 00551 return ERROR_INT("pixd not 8 bpp", procName, 1); 00552 if ((cmap = pixGetColormap(pixd)) == NULL) 00553 return ERROR_INT("cmap not found", procName, 1); 00554 if (!pixs) 00555 return ERROR_INT("pixs not defined", procName, 1); 00556 ncolors = pixcmapGetCount(cmap); 00557 if (finalcolors >= ncolors) /* few enough colors already; nothing to do */ 00558 return 0; 00559 00560 /* Generate a mask over all pixels that are not in the 00561 * 'finalcolors' most populated colors. Save the colormap 00562 * index of any one of the retained colors in 'tempindex'. 00563 * The LUT has values 0 for the 'finalcolors' most populated colors, 00564 * which will be retained; and 1 for the rest, which are marked 00565 * by fg pixels in pixm and will be removed. */ 00566 na = pixGetCmapHistogram(pixd, 1); 00567 if ((nasi = numaGetSortIndex(na, L_SORT_DECREASING)) == NULL) { 00568 numaDestroy(&na); 00569 return ERROR_INT("nasi not made", procName, 1); 00570 } 00571 numaGetIValue(nasi, finalcolors - 1, &tempindex); /* retain down to this */ 00572 pixcmapGetColor32(cmap, tempindex, &tempcolor); /* use this color */ 00573 tab = (l_int32 *)CALLOC(256, sizeof(l_int32)); 00574 for (i = finalcolors; i < ncolors; i++) { 00575 numaGetIValue(nasi, i, &index); 00576 tab[index] = 1; 00577 } 00578 00579 pixm = pixMakeMaskFromLUT(pixd, tab); 00580 FREE(tab); 00581 00582 /* Reassign the masked pixels temporarily to the saved index 00583 * (tempindex). This guarantees that no pixels are labeled by 00584 * a colormap index of any colors that will be removed. 00585 * The actual value doesn't matter, as long as it's one 00586 * of the retained colors, because these pixels will later 00587 * be reassigned based on the full set of colors retained 00588 * in the colormap. */ 00589 pixSetMasked(pixd, pixm, tempcolor); 00590 00591 /* Now remove unused colors from the colormap. This reassigns 00592 * image pixels as required. */ 00593 pixRemoveUnusedColors(pixd); 00594 00595 /* Finally, reassign the pixels under the mask (those that were 00596 * given a 'tempindex' value) to the nearest color in the colormap. 00597 * This is the function used in phase 2 on all image pixels; here 00598 * it is only used on the masked pixels given by pixm. */ 00599 pixAssignToNearestColor(pixd, pixs, pixm, LEVEL_IN_OCTCUBE, NULL); 00600 00601 pixDestroy(&pixm); 00602 numaDestroy(&na); 00603 numaDestroy(&nasi); 00604 return 0; 00605 } 00606