Leptonica  1.77.0
Image processing and image analysis suite
colorseg.c
Go to the documentation of this file.
1 /*====================================================================*
2  - Copyright (C) 2001 Leptonica. All rights reserved.
3  -
4  - Redistribution and use in source and binary forms, with or without
5  - modification, are permitted provided that the following conditions
6  - are met:
7  - 1. Redistributions of source code must retain the above copyright
8  - notice, this list of conditions and the following disclaimer.
9  - 2. Redistributions in binary form must reproduce the above
10  - copyright notice, this list of conditions and the following
11  - disclaimer in the documentation and/or other materials
12  - provided with the distribution.
13  -
14  - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
18  - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23  - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *====================================================================*/
26 
42 #include "allheaders.h"
43 
44  /* Maximum allowed iterations in Phase 1. */
45 static const l_int32 MAX_ALLOWED_ITERATIONS = 20;
46 
47  /* Factor by which max dist is increased on each iteration */
48 static const l_float32 DIST_EXPAND_FACT = 1.3;
49 
50  /* Octcube division level for computing nearest colormap color using LUT.
51  * Using 4 should suffice for up to 50 - 100 colors, and it is
52  * very fast. Using 5 takes 8 times as long to set up the LUT
53  * for little perceptual gain, even with 100 colors. */
54 static const l_int32 LEVEL_IN_OCTCUBE = 4;
55 
56 
57 static l_int32 pixColorSegmentTryCluster(PIX *pixd, PIX *pixs,
58  l_int32 maxdist, l_int32 maxcolors,
59  l_int32 debugflag);
60 
61 /*------------------------------------------------------------------*
62  * Unsupervised color segmentation *
63  *------------------------------------------------------------------*/
128 PIX *
130  l_int32 maxdist,
131  l_int32 maxcolors,
132  l_int32 selsize,
133  l_int32 finalcolors,
134  l_int32 debugflag)
135 {
136 l_int32 *countarray;
137 PIX *pixd;
138 
139  PROCNAME("pixColorSegment");
140 
141  if (!pixs)
142  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
143  if (pixGetDepth(pixs) != 32)
144  return (PIX *)ERROR_PTR("must be rgb color", procName, NULL);
145 
146  /* Phase 1; original segmentation */
147  pixd = pixColorSegmentCluster(pixs, maxdist, maxcolors, debugflag);
148  if (!pixd)
149  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
150  if (debugflag) {
151  lept_mkdir("lept/segment");
152  pixWriteDebug("/tmp/lept/segment/colorseg1.png", pixd, IFF_PNG);
153  }
154 
155  /* Phase 2; refinement in pixel assignment */
156  if ((countarray = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL) {
157  pixDestroy(&pixd);
158  return (PIX *)ERROR_PTR("countarray not made", procName, NULL);
159  }
160  pixAssignToNearestColor(pixd, pixs, NULL, LEVEL_IN_OCTCUBE, countarray);
161  if (debugflag)
162  pixWriteDebug("/tmp/lept/segment/colorseg2.png", pixd, IFF_PNG);
163 
164  /* Phase 3: noise removal by separately closing each color */
165  pixColorSegmentClean(pixd, selsize, countarray);
166  LEPT_FREE(countarray);
167  if (debugflag)
168  pixWriteDebug("/tmp/lept/segment/colorseg3.png", pixd, IFF_PNG);
169 
170  /* Phase 4: removal of colors with small population and
171  * reassignment of pixels to remaining colors */
172  pixColorSegmentRemoveColors(pixd, pixs, finalcolors);
173  return pixd;
174 }
175 
176 
199 PIX *
201  l_int32 maxdist,
202  l_int32 maxcolors,
203  l_int32 debugflag)
204 {
205 l_int32 w, h, newmaxdist, ret, niters, ncolors, success;
206 PIX *pixd;
207 PIXCMAP *cmap;
208 
209  PROCNAME("pixColorSegmentCluster");
210 
211  if (!pixs)
212  return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
213  if (pixGetDepth(pixs) != 32)
214  return (PIX *)ERROR_PTR("must be rgb color", procName, NULL);
215 
216  pixGetDimensions(pixs, &w, &h, NULL);
217  if ((pixd = pixCreate(w, h, 8)) == NULL)
218  return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
219  cmap = pixcmapCreate(8);
220  pixSetColormap(pixd, cmap);
221  pixCopyResolution(pixd, pixs);
222 
223  newmaxdist = maxdist;
224  niters = 0;
225  success = TRUE;
226  while (1) {
227  ret = pixColorSegmentTryCluster(pixd, pixs, newmaxdist,
228  maxcolors, debugflag);
229  niters++;
230  if (!ret) {
231  ncolors = pixcmapGetCount(cmap);
232  if (debugflag)
233  L_INFO("Success with %d colors after %d iters\n", procName,
234  ncolors, niters);
235  break;
236  }
237  if (niters == MAX_ALLOWED_ITERATIONS) {
238  L_WARNING("too many iters; newmaxdist = %d\n",
239  procName, newmaxdist);
240  success = FALSE;
241  break;
242  }
243  newmaxdist = (l_int32)(DIST_EXPAND_FACT * (l_float32)newmaxdist);
244  }
245 
246  if (!success) {
247  pixDestroy(&pixd);
248  return (PIX *)ERROR_PTR("failure in phase 1", procName, NULL);
249  }
250 
251  return pixd;
252 }
253 
254 
270 static l_int32
272  PIX *pixs,
273  l_int32 maxdist,
274  l_int32 maxcolors,
275  l_int32 debugflag)
276 {
277 l_int32 rmap[256], gmap[256], bmap[256];
278 l_int32 w, h, wpls, wpld, i, j, k, found, ret, index, ncolors;
279 l_int32 rval, gval, bval, dist2, maxdist2;
280 l_int32 countarray[256];
281 l_int32 rsum[256], gsum[256], bsum[256];
282 l_uint32 *ppixel;
283 l_uint32 *datas, *datad, *lines, *lined;
284 PIXCMAP *cmap;
285 
286  PROCNAME("pixColorSegmentTryCluster");
287 
288  if (!pixs)
289  return ERROR_INT("pixs not defined", procName, 1);
290  if (!pixd)
291  return ERROR_INT("pixd not defined", procName, 1);
292 
293  w = pixGetWidth(pixs);
294  h = pixGetHeight(pixs);
295  maxdist2 = maxdist * maxdist;
296  cmap = pixGetColormap(pixd);
297  pixcmapClear(cmap);
298  for (k = 0; k < 256; k++) {
299  rsum[k] = gsum[k] = bsum[k] = 0;
300  rmap[k] = gmap[k] = bmap[k] = 0;
301  }
302 
303  datas = pixGetData(pixs);
304  datad = pixGetData(pixd);
305  wpls = pixGetWpl(pixs);
306  wpld = pixGetWpl(pixd);
307  ncolors = 0;
308  for (i = 0; i < h; i++) {
309  lines = datas + i * wpls;
310  lined = datad + i * wpld;
311  for (j = 0; j < w; j++) {
312  ppixel = lines + j;
313  rval = GET_DATA_BYTE(ppixel, COLOR_RED);
314  gval = GET_DATA_BYTE(ppixel, COLOR_GREEN);
315  bval = GET_DATA_BYTE(ppixel, COLOR_BLUE);
316  ncolors = pixcmapGetCount(cmap);
317  found = FALSE;
318  for (k = 0; k < ncolors; k++) {
319  dist2 = (rval - rmap[k]) * (rval - rmap[k]) +
320  (gval - gmap[k]) * (gval - gmap[k]) +
321  (bval - bmap[k]) * (bval - bmap[k]);
322  if (dist2 <= maxdist2) { /* take it; greedy */
323  found = TRUE;
324  SET_DATA_BYTE(lined, j, k);
325  countarray[k]++;
326  rsum[k] += rval;
327  gsum[k] += gval;
328  bsum[k] += bval;
329  break;
330  }
331  }
332  if (!found) { /* Add a new color */
333  ret = pixcmapAddNewColor(cmap, rval, gval, bval, &index);
334 /* fprintf(stderr,
335  "index = %d, (i,j) = (%d,%d), rgb = (%d, %d, %d)\n",
336  index, i, j, rval, gval, bval); */
337  if (ret == 0 && index < maxcolors) {
338  countarray[index] = 1;
339  SET_DATA_BYTE(lined, j, index);
340  rmap[index] = rval;
341  gmap[index] = gval;
342  bmap[index] = bval;
343  rsum[index] = rval;
344  gsum[index] = gval;
345  bsum[index] = bval;
346  } else {
347  if (debugflag) {
348  L_INFO("maxcolors exceeded for maxdist = %d\n",
349  procName, maxdist);
350  }
351  return 1;
352  }
353  }
354  }
355  }
356 
357  /* Replace the colors in the colormap by the averages */
358  for (k = 0; k < ncolors; k++) {
359  rval = rsum[k] / countarray[k];
360  gval = gsum[k] / countarray[k];
361  bval = bsum[k] / countarray[k];
362  pixcmapResetColor(cmap, k, rval, gval, bval);
363  }
364 
365  return 0;
366 }
367 
368 
411 l_ok
413  PIX *pixs,
414  PIX *pixm,
415  l_int32 level,
416  l_int32 *countarray)
417 {
418 l_int32 w, h, wpls, wpld, wplm, i, j, success;
419 l_int32 rval, gval, bval, index;
420 l_int32 *cmaptab;
421 l_uint32 octindex;
422 l_uint32 *rtab, *gtab, *btab;
423 l_uint32 *ppixel;
424 l_uint32 *datas, *datad, *datam, *lines, *lined, *linem;
425 PIXCMAP *cmap;
426 
427  PROCNAME("pixAssignToNearestColor");
428 
429  if (!pixd)
430  return ERROR_INT("pixd not defined", procName, 1);
431  if ((cmap = pixGetColormap(pixd)) == NULL)
432  return ERROR_INT("cmap not found", procName, 1);
433  if (!pixs)
434  return ERROR_INT("pixs not defined", procName, 1);
435  if (pixGetDepth(pixs) != 32)
436  return ERROR_INT("pixs not 32 bpp", procName, 1);
437  if (level < 1 || level > 6)
438  return ERROR_INT("level not in [1 ... 6]", procName, 1);
439 
440  /* Set up the tables to map rgb to the nearest colormap index */
441  success = TRUE;
442  makeRGBToIndexTables(&rtab, &gtab, &btab, level);
443  cmaptab = pixcmapToOctcubeLUT(cmap, level, L_MANHATTAN_DISTANCE);
444  if (!rtab || !gtab || !btab || !cmaptab) {
445  L_ERROR("failure to make a table\n", procName);
446  success = FALSE;
447  goto cleanup_arrays;
448  }
449 
450  pixGetDimensions(pixs, &w, &h, NULL);
451  datas = pixGetData(pixs);
452  datad = pixGetData(pixd);
453  wpls = pixGetWpl(pixs);
454  wpld = pixGetWpl(pixd);
455  if (pixm) {
456  datam = pixGetData(pixm);
457  wplm = pixGetWpl(pixm);
458  }
459  for (i = 0; i < h; i++) {
460  lines = datas + i * wpls;
461  lined = datad + i * wpld;
462  if (pixm)
463  linem = datam + i * wplm;
464  for (j = 0; j < w; j++) {
465  if (pixm) {
466  if (!GET_DATA_BIT(linem, j))
467  continue;
468  }
469  ppixel = lines + j;
470  rval = GET_DATA_BYTE(ppixel, COLOR_RED);
471  gval = GET_DATA_BYTE(ppixel, COLOR_GREEN);
472  bval = GET_DATA_BYTE(ppixel, COLOR_BLUE);
473  /* Map from rgb to octcube index */
474  getOctcubeIndexFromRGB(rval, gval, bval, rtab, gtab, btab,
475  &octindex);
476  /* Map from octcube index to nearest colormap index */
477  index = cmaptab[octindex];
478  if (countarray)
479  countarray[index]++;
480  SET_DATA_BYTE(lined, j, index);
481  }
482  }
483 
484 cleanup_arrays:
485  LEPT_FREE(cmaptab);
486  LEPT_FREE(rtab);
487  LEPT_FREE(gtab);
488  LEPT_FREE(btab);
489  return (success) ? 0 : 1;
490 }
491 
492 
511 l_ok
513  l_int32 selsize,
514  l_int32 *countarray)
515 {
516 l_int32 i, ncolors, val;
517 l_uint32 val32;
518 NUMA *na, *nasi;
519 PIX *pixt1, *pixt2;
520 PIXCMAP *cmap;
521 
522  PROCNAME("pixColorSegmentClean");
523 
524  if (!pixs)
525  return ERROR_INT("pixs not defined", procName, 1);
526  if (pixGetDepth(pixs) != 8)
527  return ERROR_INT("pixs not 8 bpp", procName, 1);
528  if ((cmap = pixGetColormap(pixs)) == NULL)
529  return ERROR_INT("cmap not found", procName, 1);
530  if (!countarray)
531  return ERROR_INT("countarray not defined", procName, 1);
532  if (selsize <= 1)
533  return 0; /* nothing to do */
534 
535  /* Sort colormap indices in decreasing order of pixel population */
536  ncolors = pixcmapGetCount(cmap);
537  na = numaCreate(ncolors);
538  for (i = 0; i < ncolors; i++)
539  numaAddNumber(na, countarray[i]);
541  numaDestroy(&na);
542  if (!nasi)
543  return ERROR_INT("nasi not made", procName, 1);
544 
545  /* For each color, in order of decreasing population,
546  * do a closing and absorb the added pixels. Note that
547  * if the closing removes pixels at the border, they'll
548  * still appear in the xor and will be properly (re)set. */
549  for (i = 0; i < ncolors; i++) {
550  numaGetIValue(nasi, i, &val);
551  pixt1 = pixGenerateMaskByValue(pixs, val, 1);
552  pixt2 = pixCloseSafeCompBrick(NULL, pixt1, selsize, selsize);
553  pixXor(pixt2, pixt2, pixt1); /* pixels to be added to type 'val' */
554  pixcmapGetColor32(cmap, val, &val32);
555  pixSetMasked(pixs, pixt2, val32); /* add them */
556  pixDestroy(&pixt1);
557  pixDestroy(&pixt2);
558  }
559  numaDestroy(&nasi);
560  return 0;
561 }
562 
563 
583 l_ok
585  PIX *pixs,
586  l_int32 finalcolors)
587 {
588 l_int32 i, ncolors, index, tempindex;
589 l_int32 *tab;
590 l_uint32 tempcolor;
591 NUMA *na, *nasi;
592 PIX *pixm;
593 PIXCMAP *cmap;
594 
595  PROCNAME("pixColorSegmentRemoveColors");
596 
597  if (!pixd)
598  return ERROR_INT("pixd not defined", procName, 1);
599  if (pixGetDepth(pixd) != 8)
600  return ERROR_INT("pixd not 8 bpp", procName, 1);
601  if ((cmap = pixGetColormap(pixd)) == NULL)
602  return ERROR_INT("cmap not found", procName, 1);
603  if (!pixs)
604  return ERROR_INT("pixs not defined", procName, 1);
605  ncolors = pixcmapGetCount(cmap);
606  if (finalcolors >= ncolors) /* few enough colors already; nothing to do */
607  return 0;
608 
609  /* Generate a mask over all pixels that are not in the
610  * 'finalcolors' most populated colors. Save the colormap
611  * index of any one of the retained colors in 'tempindex'.
612  * The LUT has values 0 for the 'finalcolors' most populated colors,
613  * which will be retained; and 1 for the rest, which are marked
614  * by fg pixels in pixm and will be removed. */
615  na = pixGetCmapHistogram(pixd, 1);
616  if ((nasi = numaGetSortIndex(na, L_SORT_DECREASING)) == NULL) {
617  numaDestroy(&na);
618  return ERROR_INT("nasi not made", procName, 1);
619  }
620  numaGetIValue(nasi, finalcolors - 1, &tempindex); /* retain down to this */
621  pixcmapGetColor32(cmap, tempindex, &tempcolor); /* use this color */
622  tab = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32));
623  for (i = finalcolors; i < ncolors; i++) {
624  numaGetIValue(nasi, i, &index);
625  tab[index] = 1;
626  }
627 
628  pixm = pixMakeMaskFromLUT(pixd, tab);
629  LEPT_FREE(tab);
630 
631  /* Reassign the masked pixels temporarily to the saved index
632  * (tempindex). This guarantees that no pixels are labeled by
633  * a colormap index of any colors that will be removed.
634  * The actual value doesn't matter, as long as it's one
635  * of the retained colors, because these pixels will later
636  * be reassigned based on the full set of colors retained
637  * in the colormap. */
638  pixSetMasked(pixd, pixm, tempcolor);
639 
640  /* Now remove unused colors from the colormap. This reassigns
641  * image pixels as required. */
642  pixRemoveUnusedColors(pixd);
643 
644  /* Finally, reassign the pixels under the mask (those that were
645  * given a 'tempindex' value) to the nearest color in the colormap.
646  * This is the function used in phase 2 on all image pixels; here
647  * it is only used on the masked pixels given by pixm. */
648  pixAssignToNearestColor(pixd, pixs, pixm, LEVEL_IN_OCTCUBE, NULL);
649 
650  pixDestroy(&pixm);
651  numaDestroy(&na);
652  numaDestroy(&nasi);
653  return 0;
654 }
l_int32 lept_mkdir(const char *subdir)
lept_mkdir()
Definition: utils2.c:1944
NUMA * numaGetSortIndex(NUMA *na, l_int32 sortorder)
numaGetSortIndex()
Definition: numafunc1.c:2637
l_int32 * pixcmapToOctcubeLUT(PIXCMAP *cmap, l_int32 level, l_int32 metric)
pixcmapToOctcubeLUT()
Definition: colorquant1.c:3858
l_ok pixSetMasked(PIX *pixd, PIX *pixm, l_uint32 val)
pixSetMasked()
Definition: pix3.c:155
l_ok numaAddNumber(NUMA *na, l_float32 val)
numaAddNumber()
Definition: numabasic.c:473
NUMA * pixGetCmapHistogram(PIX *pixs, l_int32 factor)
pixGetCmapHistogram()
Definition: pix4.c:627
PIX * pixCloseSafeCompBrick(PIX *pixd, PIX *pixs, l_int32 hsize, l_int32 vsize)
pixCloseSafeCompBrick()
Definition: morph.c:1598
l_ok makeRGBToIndexTables(l_uint32 **prtab, l_uint32 **pgtab, l_uint32 **pbtab, l_int32 cqlevels)
makeRGBToIndexTables()
Definition: colorquant1.c:1361
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:302
NUMA * numaCreate(l_int32 n)
numaCreate()
Definition: numabasic.c:187
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1624
PIX * pixGenerateMaskByValue(PIX *pixs, l_int32 val, l_int32 usecmap)
pixGenerateMaskByValue()
Definition: grayquant.c:811
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
l_ok pixAssignToNearestColor(PIX *pixd, PIX *pixs, PIX *pixm, l_int32 level, l_int32 *countarray)
pixAssignToNearestColor()
Definition: colorseg.c:412
l_ok pixSetColormap(PIX *pix, PIXCMAP *colormap)
pixSetColormap()
Definition: pix1.c:1573
PIXCMAP * pixcmapCreate(l_int32 depth)
pixcmapCreate()
Definition: colormap.c:111
l_ok pixColorSegmentRemoveColors(PIX *pixd, PIX *pixs, l_int32 finalcolors)
pixColorSegmentRemoveColors()
Definition: colorseg.c:584
l_ok numaGetIValue(NUMA *na, l_int32 index, l_int32 *pival)
numaGetIValue()
Definition: numabasic.c:727
Definition: array.h:59
PIX * pixXor(PIX *pixd, PIX *pixs1, PIX *pixs2)
pixXor()
Definition: pix3.c:1574
void getOctcubeIndexFromRGB(l_int32 rval, l_int32 gval, l_int32 bval, l_uint32 *rtab, l_uint32 *gtab, l_uint32 *btab, l_uint32 *pindex)
getOctcubeIndexFromRGB()
Definition: colorquant1.c:1470
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
l_ok pixcmapAddNewColor(PIXCMAP *cmap, l_int32 rval, l_int32 gval, l_int32 bval, l_int32 *pindex)
pixcmapAddNewColor()
Definition: colormap.c:423
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
PIX * pixColorSegmentCluster(PIX *pixs, l_int32 maxdist, l_int32 maxcolors, l_int32 debugflag)
pixColorSegmentCluster()
Definition: colorseg.c:200
static l_int32 pixColorSegmentTryCluster(PIX *pixd, PIX *pixs, l_int32 maxdist, l_int32 maxcolors, l_int32 debugflag)
pixColorSegmentTryCluster()
Definition: colorseg.c:271
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:543
l_ok pixcmapResetColor(PIXCMAP *cmap, l_int32 index, l_int32 rval, l_int32 gval, l_int32 bval)
pixcmapResetColor()
Definition: colormap.c:893
void numaDestroy(NUMA **pna)
numaDestroy()
Definition: numabasic.c:360
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1065
l_ok pixColorSegmentClean(PIX *pixs, l_int32 selsize, l_int32 *countarray)
pixColorSegmentClean()
Definition: colorseg.c:512
PIX * pixColorSegment(PIX *pixs, l_int32 maxdist, l_int32 maxcolors, l_int32 selsize, l_int32 finalcolors, l_int32 debugflag)
pixColorSegment()
Definition: colorseg.c:129
l_ok pixcmapGetColor32(PIXCMAP *cmap, l_int32 index, l_uint32 *pval32)
pixcmapGetColor32()
Definition: colormap.c:791
PIX * pixMakeMaskFromLUT(PIX *pixs, l_int32 *tab)
pixMakeMaskFromLUT()
Definition: pix3.c:1000
l_ok pixRemoveUnusedColors(PIX *pixs)
pixRemoveUnusedColors()
Definition: colorquant1.c:3944
l_int32 pixcmapGetCount(PIXCMAP *cmap)
pixcmapGetCount()
Definition: colormap.c:635
Definition: pix.h:134
Definition: pix.h:201
l_ok pixcmapClear(PIXCMAP *cmap)
pixcmapClear()
Definition: colormap.c:728