/*! * \brief pixColorSegment() * * \param[in] pixs 32 bpp; 24-bit color * \param[in] maxdist max euclidean dist to existing cluster * \param[in] maxcolors max number of colors allowed in first pass * \param[in] selsize linear size of sel for closing to remove noise * \param[in] finalcolors max number of final colors allowed after 4th pass * \param[in] debugflag 1 for debug output; 0 otherwise * \return pixd 8 bit with colormap, or NULL on error * * <pre> * Color segmentation proceeds in four phases: * * Phase 1: pixColorSegmentCluster() * The image is traversed in raster order. Each pixel either * becomes the representative for a new cluster or is assigned to an * existing cluster. Assignment is greedy. The data is stored in * a colormapped image. Three auxiliary arrays are used to hold * the colors of the representative pixels, for fast lookup. * The average color in each cluster is computed. * * Phase 2. pixAssignToNearestColor() * A second non-greedy clustering pass is performed, where each pixel * is assigned to the nearest cluster average. We also keep track * of how many pixels are assigned to each cluster. * * Phase 3. pixColorSegmentClean() * For each cluster, starting with the largest, do a morphological * closing to eliminate small components within larger ones. * * Phase 4. pixColorSegmentRemoveColors() * Eliminate all colors except the most populated 'finalcolors'. * Then remove unused colors from the colormap, and reassign those * pixels to the nearest remaining cluster, using the original pixel values. * * Notes: * (1) The goal is to generate a small number of colors. * Typically this would be specified by 'finalcolors', * a number that would be somewhere between 3 and 6. * The parameter 'maxcolors' specifies the maximum number of * colors generated in the first phase. This should be * larger than finalcolors, perhaps twice as large. * If more than 'maxcolors' are generated in the first phase * using the input 'maxdist', the distance is repeatedly * increased by a multiplicative factor until the condition * is satisfied. The implicit relation between 'maxdist' * and 'maxcolors' is thus adjusted programmatically. * (2) As a very rough guideline, given a target value of 'finalcolors', * here are approximate values of 'maxdist' and 'maxcolors' * to start with: * * finalcolors maxcolors maxdist * ----------- --------- ------- * 3 6 100 * 4 8 90 * 5 10 75 * 6 12 60 * * For a given number of finalcolors, if you use too many * maxcolors, the result will be noisy. If you use too few, * the result will be a relatively poor assignment of colors. * </pre> */ PIX * pixColorSegment(PIX *pixs, l_int32 maxdist, l_int32 maxcolors, l_int32 selsize, l_int32 finalcolors, l_int32 debugflag) { l_int32 *countarray; PIX *pixd; PROCNAME("pixColorSegment"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("must be rgb color", procName, NULL); /* Phase 1; original segmentation */ pixd = pixColorSegmentCluster(pixs, maxdist, maxcolors, debugflag); if (!pixd) return (PIX *)ERROR_PTR("pixd not made", procName, NULL); if (debugflag) { lept_mkdir("lept/segment"); pixWriteDebug("/tmp/lept/segment/colorseg1.png", pixd, IFF_PNG); } /* Phase 2; refinement in pixel assignment */ if ((countarray = (l_int32 *)LEPT_CALLOC(256, sizeof(l_int32))) == NULL) { pixDestroy(&pixd); return (PIX *)ERROR_PTR("countarray not made", procName, NULL); } pixAssignToNearestColor(pixd, pixs, NULL, LEVEL_IN_OCTCUBE, countarray); if (debugflag) pixWriteDebug("/tmp/lept/segment/colorseg2.png", pixd, IFF_PNG); /* Phase 3: noise removal by separately closing each color */ pixColorSegmentClean(pixd, selsize, countarray); LEPT_FREE(countarray); if (debugflag) pixWriteDebug("/tmp/lept/segment/colorseg3.png", pixd, IFF_PNG); /* Phase 4: removal of colors with small population and * reassignment of pixels to remaining colors */ pixColorSegmentRemoveColors(pixd, pixs, finalcolors); return pixd; }
/*! * pixColorSegment() * * Input: pixs (32 bpp; 24-bit color) * maxdist (max euclidean dist to existing cluster) * maxcolors (max number of colors allowed in first pass) * selsize (linear size of sel for closing to remove noise) * finalcolors (max number of final colors allowed after 4th pass) * Return: pixd (8 bit with colormap), or null on error * * Color segmentation proceeds in four phases: * * Phase 1: pixColorSegmentCluster() * The image is traversed in raster order. Each pixel either * becomes the representative for a new cluster or is assigned to an * existing cluster. Assignment is greedy. The data is stored in * a colormapped image. Three auxiliary arrays are used to hold * the colors of the representative pixels, for fast lookup. * The average color in each cluster is computed. * * Phase 2. pixAssignToNearestColor() * A second (non-greedy) clustering pass is performed, where each pixel * is assigned to the nearest cluster (average). We also keep track * of how many pixels are assigned to each cluster. * * Phase 3. pixColorSegmentClean() * For each cluster, starting with the largest, do a morphological * closing to eliminate small components within larger ones. * * Phase 4. pixColorSegmentRemoveColors() * Eliminate all colors except the most populated 'finalcolors'. * Then remove unused colors from the colormap, and reassign those * pixels to the nearest remaining cluster, using the original pixel values. * * Notes: * (1) The goal is to generate a small number of colors. * Typically this would be specified by 'finalcolors', * a number that would be somewhere between 3 and 6. * The parameter 'maxcolors' specifies the maximum number of * colors generated in the first phase. This should be * larger than finalcolors, perhaps twice as large. * If more than 'maxcolors' are generated in the first phase * using the input 'maxdist', the distance is repeatedly * increased by a multiplicative factor until the condition * is satisfied. The implicit relation between 'maxdist' * and 'maxcolors' is thus adjusted programmatically. * (2) As a very rough guideline, given a target value of 'finalcolors', * here are approximate values of 'maxdist' and 'maxcolors' * to start with: * * finalcolors maxcolors maxdist * ----------- --------- ------- * 3 6 100 * 4 8 90 * 5 10 75 * 6 12 60 * * For a given number of finalcolors, if you use too many * maxcolors, the result will be noisy. If you use too few, * the result will be a relatively poor assignment of colors. */ PIX * pixColorSegment(PIX *pixs, l_int32 maxdist, l_int32 maxcolors, l_int32 selsize, l_int32 finalcolors) { l_int32 *countarray; PIX *pixd; PROCNAME("pixColorSegment"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("must be rgb color", procName, NULL); /* Phase 1; original segmentation */ if ((pixd = pixColorSegmentCluster(pixs, maxdist, maxcolors)) == NULL) return (PIX *)ERROR_PTR("pixt1 not made", procName, NULL); /* pixWrite("junkpixd1", pixd, IFF_PNG); */ /* Phase 2; refinement in pixel assignment */ if ((countarray = (l_int32 *)CALLOC(256, sizeof(l_int32))) == NULL) return (PIX *)ERROR_PTR("countarray not made", procName, NULL); pixAssignToNearestColor(pixd, pixs, NULL, LEVEL_IN_OCTCUBE, countarray); /* pixWrite("junkpixd2", pixd, IFF_PNG); */ /* Phase 3: noise removal by separately closing each color */ pixColorSegmentClean(pixd, selsize, countarray); /* pixWrite("junkpixd3", pixd, IFF_PNG); */ FREE(countarray); /* Phase 4: removal of colors with small population and * reassignment of pixels to remaining colors */ pixColorSegmentRemoveColors(pixd, pixs, finalcolors); return pixd; }