From: ishmal Date: Fri, 24 Mar 2006 14:40:42 +0000 (+0000) Subject: Add siox X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=3823b0c996be544aa088e6f03c6e189b66d79a5c;p=inkscape.git Add siox --- diff --git a/src/trace/Makefile_insert b/src/trace/Makefile_insert index 92686be0b..6a2100207 100644 --- a/src/trace/Makefile_insert +++ b/src/trace/Makefile_insert @@ -12,8 +12,10 @@ trace_libtrace_a_SOURCES = \ trace/imagemap-gdk.h \ trace/imagemap.cpp \ trace/imagemap.h \ - trace/filterset.cpp \ trace/filterset.h \ + trace/filterset.cpp \ + trace/siox-segmentator.h \ + trace/siox-segmentator.cpp \ trace/potrace/auxiliary.h \ trace/potrace/bitmap.h \ trace/potrace/curve.cpp \ diff --git a/src/trace/siox-segmentator.cpp b/src/trace/siox-segmentator.cpp new file mode 100644 index 000000000..31d99ca03 --- /dev/null +++ b/src/trace/siox-segmentator.cpp @@ -0,0 +1,1371 @@ +/* + Copyright 2005, 2006 by Gerald Friedland, Kristian Jantz and Lars Knipping + + Conversion to C++ for Inkscape by Bob Jamison + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +#include "siox-segmentator.h" + +#include + +#include //for error() and trace() +#include //sqrt(), pow(), round(), etc + + +namespace org +{ +namespace siox +{ + +//######################################################################## +//## U T I L S (originally Utils.java) +//######################################################################## + +/** + * Collection of auxiliary image processing methods used by the + * SioxSegmentator mainly for postprocessing. + * + * @author G. Friedland, K. Jantz, L. Knipping + * @version 1.05 + * + * Conversion to C++ by Bob Jamison + * + */ + + +/** Caches color conversion values to speed up RGB->CIELAB conversion.*/ +static std::map RGB_TO_LAB; + +//forward decls +static void premultiplyMatrix(float alpha, float *cm, int cmSize); +//static float colordiffsq(long rgb0, long rgb1); +//static int getAlpha(long argb); +static int getRed(long rgb); +static int getGreen(long rgb); +static int getBlue(long rgb); +//static long packPixel(int a, int r, int g, int b); +static CLAB rgbToClab(long rgb); + +/** + * Applies the morphological dilate operator. + * + * Can be used to close small holes in the given confidence matrix. + * + * @param cm Confidence matrix to be processed. + * @param xres Horizontal resolution of the matrix. + * @param yres Vertical resolution of the matrix. + */ +static void dilate(float *cm, int xres, int yres) +{ + for (int y=0; ycm[idx]) + cm[idx]=cm[idx+1]; + } + } + for (int y=0; y=1; x--) { + int idx=(y*xres)+x; + if (cm[idx-1]>cm[idx]) + cm[idx]=cm[idx-1]; + } + } + for (int y=0; y cm[idx]) + cm[idx]=cm[((y+1)*xres)+x]; + } + } + for (int y=yres-1; y>=1; y--) { + for (int x=0; x cm[idx]) + cm[idx]=cm[((y-1)*xres)+x]; + } + } +} + +/** + * Applies the morphological erode operator. + * + * @param cm Confidence matrix to be processed. + * @param xres Horizontal resolution of the matrix. + * @param yres Vertical resolution of the matrix. + */ +static void erode(float *cm, int xres, int yres) +{ + for (int y=0; y=1; x--) { + int idx=(y*xres)+x; + if (cm[idx-1] < cm[idx]) + cm[idx]=cm[idx-1]; + } + } + for (int y=0; y=1; y--) { + for (int x=0; x + * In the standard case confidence matrix entries are between 0...1 and + * the weight factors sum up to 1. + * + * @param cm The matrix to be smoothed. + * @param xres Horizontal resolution of the matrix. + * @param yres Vertical resolution of the matrix. + * @param f1 Weight factor for the first pixel. + * @param f2 Weight factor for the mid-pixel. + * @param f3 Weight factor for the last pixel. + */ +static void smoothcm(float *cm, int xres, int yres, + float f1, float f2, float f3) +{ + for (int y=0; y=2; x--) { + int idx=(y*xres)+x; + cm[idx]=f3*cm[idx-2]+f2*cm[idx-1]+f1*cm[idx]; + } + } + for (int y=0; y=2; y--) { + for (int x=0; x + * Usage hint: When only comparisons between Euclidian distances without + * actual values are needed, the squared distance can be preferred + * for being faster to compute. + * + * @param p First euclidian point coordinates. + * @param pSize Length of coordinate array. + * @param q Second euclidian point coordinates. + * Dimension must not be smaller than that of p. + * Any extra dimensions will be ignored. + * @return Squared euclidian distance of p and q. + * @see #euclid + */ +static float sqrEuclidianDist(float *p, int pSize, float *q) +{ + float sum=0; + for (int i=0; i + * Usage hint: When only comparisons between Euclidian distances without + * actual values are needed, the squared distance can be preferred + * for being faster to compute. + * + * @param p CLAB value + * @param q second CLAB value + * @return Squared euclidian distance of p and q. + * @see #euclid + */ +static float sqrEuclidianDist(const CLAB &p, const CLAB &q) +{ + float sum=0; + sum += (p.C - q.C) * (p.C - q.C); + sum += (p.L - q.L) * (p.L - q.L); + sum += (p.A - q.A) * (p.A - q.A); + sum += (p.B - q.B) * (p.B - q.B); + return sum; +} + +/** + * Euclidian distance of p and q. + * + * @param p First euclidian point coordinates. + * @param pSize Length of coordinate array. + * @param q Second euclidian point coordinates. + * Dimension must not be smaller than that of p. + * Any extra dimensions will be ignored. + * @return Squared euclidian distance of p and q. + * @see #sqrEuclidianDist + */ +/* +static float euclid(float *p, int pSize, float *q) +{ + return (float)sqrt(sqrEuclidianDist(p, pSize, q)); +} +*/ + +/** + * Computes Euclidian distance of two RGB color values. + * + * @param rgb0 First color value. + * @param rgb1 Second color value. + * @return Euclidian distance between the two color values. + */ +/* +static float colordiff(long rgb0, long rgb1) +{ + return (float)sqrt(colordiffsq(rgb0, rgb1)); +} +*/ + +/** + * Computes squared euclidian distance of two RGB color values. + *

+ * Note: Faster to compute than colordiff + * + * @param rgb0 First color value. + * @param rgb1 Second color value. + * @return Squared Euclidian distance between the two color values. + */ +/* +static float colordiffsq(long rgb0, long rgb1) +{ + int rDist=getRed(rgb0) - getRed(rgb1); + int gDist=getGreen(rgb0) - getGreen(rgb1); + int bDist=getBlue(rgb0) - getBlue(rgb1); + + return (float)(rDist*rDist+gDist*gDist+bDist*bDist); +} +*/ + +/** + * Averages two ARGB colors. + * + * @param argb0 First color value. + * @param argb1 Second color value. + * @return The averaged ARGB color. + */ +/* +static long average(long argb0, long argb1) +{ + long ret = packPixel( + (getAlpha(argb0) + getAlpha(argb1))/2, + (getRed(argb0) + getRed(argb1) )/2, + (getGreen(argb0) + getGreen(argb1))/2, + (getBlue(argb0) + getBlue(argb1) )/2); + return ret; +} +*/ + +/** + * Computes squared euclidian distance in CLAB space for two colors + * given as RGB values. + * + * @param rgb0 First color value. + * @param rgb1 Second color value. + * @return Squared Euclidian distance in CLAB space. + */ +static float labcolordiffsq(long rgb1, long rgb2) +{ + CLAB c1 = rgbToClab(rgb1); + CLAB c2 = rgbToClab(rgb2); + float euclid=0.0f; + euclid += (c1.L - c2.L) * (c1.L - c2.L); + euclid += (c1.A - c2.A) * (c1.A - c2.A); + euclid += (c1.B - c2.B) * (c1.B - c2.B); + return euclid; +} + + +/** + * Computes squared euclidian distance in CLAB space for two colors + * given as RGB values. + * + * @param rgb0 First color value. + * @param rgb1 Second color value. + * @return Euclidian distance in CLAB space. + */ +static float labcolordiff(long rgb0, long rgb1) +{ + return (float)sqrt(labcolordiffsq(rgb0, rgb1)); +} + + +/** + * Converts 24-bit RGB values to {l,a,b} float values. + *

+ * The conversion used is decribed at + * CLAB Conversion + * for reference white D65. Note that that the conversion is computational + * expensive. Result are cached to speed up further conversion calls. + * + * @param rgb RGB color value, + * @return CLAB color value tripel. + */ +static CLAB rgbToClab(long rgb) +{ + std::map::iterator iter = RGB_TO_LAB.find(rgb); + if (iter != RGB_TO_LAB.end()) + { + CLAB res = iter->second; + return res; + } + + int R=getRed(rgb); + int G=getGreen(rgb); + int B=getBlue(rgb); + + float var_R=(R/255.0f); //R = From 0 to 255 + float var_G=(G/255.0f); //G = From 0 to 255 + float var_B=(B/255.0f); //B = From 0 to 255 + + if (var_R>0.04045) + var_R=(float) pow((var_R+0.055f)/1.055f, 2.4); + else + var_R=var_R/12.92f; + + if (var_G>0.04045) + var_G=(float) pow((var_G+0.055f)/1.055f, 2.4); + else + var_G=var_G/12.92f; + + if (var_B>0.04045) + var_B=(float) pow((var_B+0.055f)/1.055f, 2.4); + else + var_B=var_B/12.92f; + + var_R=var_R*100.0f; + var_G=var_G*100.0f; + var_B=var_B*100.0f; + + // Observer. = 2�, Illuminant = D65 + float X=var_R*0.4124f + var_G*0.3576f + var_B*0.1805f; + float Y=var_R*0.2126f + var_G*0.7152f + var_B*0.0722f; + float Z=var_R*0.0193f + var_G*0.1192f + var_B*0.9505f; + + float var_X=X/95.047f; + float var_Y=Y/100.0f; + float var_Z=Z/108.883f; + + if (var_X>0.008856f) + var_X=(float) pow(var_X, 0.3333f); + else + var_X=(7.787f*var_X)+(16.0f/116.0f); + + if (var_Y>0.008856f) + var_Y=(float) pow(var_Y, 0.3333f); + else + var_Y=(7.787f*var_Y)+(16.0f/116.0f); + + if (var_Z>0.008856f) + var_Z=(float) pow(var_Z, 0.3333f); + else + var_Z=(7.787f*var_Z)+(16.0f/116.0f); + + CLAB lab((116.0f*var_Y)-16.0f , 500.0f*(var_X-var_Y), 200.0f*(var_Y-var_Z)); + + RGB_TO_LAB[rgb] = lab; + + return lab; +} + +/** + * Converts an CLAB value to a RGB color value. + *

+ * This is the reverse operation to rgbToClab. + * @param clab CLAB value. + * @return RGB value. + * @see #rgbToClab + */ +/* +static long clabToRGB(const CLAB &clab) +{ + float L=clab.L; + float a=clab.A; + float b=clab.B; + + float var_Y=(L+16.0f)/116.0f; + float var_X=a/500.0f+var_Y; + float var_Z=var_Y-b/200.0f; + + float var_yPow3=(float)pow(var_Y, 3.0); + float var_xPow3=(float)pow(var_X, 3.0); + float var_zPow3=(float)pow(var_Z, 3.0); + + if (var_yPow3>0.008856f) + var_Y=var_yPow3; + else + var_Y=(var_Y-16.0f/116.0f)/7.787f; + + if (var_xPow3>0.008856f) + var_X=var_xPow3; + else + var_X=(var_X-16.0f/116.0f)/7.787f; + + if (var_zPow3>0.008856f) + var_Z=var_zPow3; + else + var_Z=(var_Z-16.0f/116.0f)/7.787f; + + float X=95.047f * var_X; //ref_X= 95.047 Observer=2�, Illuminant=D65 + float Y=100.0f * var_Y; //ref_Y=100.000 + float Z=108.883f * var_Z; //ref_Z=108.883 + + var_X=X/100.0f; //X = From 0 to ref_X + var_Y=Y/100.0f; //Y = From 0 to ref_Y + var_Z=Z/100.0f; //Z = From 0 to ref_Y + + float var_R=(float)(var_X * 3.2406f + var_Y * -1.5372f + var_Z * -0.4986f); + float var_G=(float)(var_X * -0.9689f + var_Y * 1.8758f + var_Z * 0.0415f); + float var_B=(float)(var_X * 0.0557f + var_Y * -0.2040f + var_Z * 1.0570f); + + if (var_R>0.0031308f) + var_R=(float)(1.055f*pow(var_R, (1.0f/2.4f))-0.055f); + else + var_R=12.92f*var_R; + + if (var_G>0.0031308f) + var_G=(float)(1.055f*pow(var_G, (1.0f/2.4f))-0.055f); + else + var_G=12.92f*var_G; + + if (var_B>0.0031308f) + var_B=(float)(1.055f*pow(var_B, (1.0f/2.4f))-0.055f); + else + var_B=12.92f*var_B; + + int R = (int)lround(var_R*255.0f); + int G = (int)lround(var_G*255.0f); + int B = (int)lround(var_B*255.0f); + + return packPixel(0xFF, R, G, B); +} +*/ + +/** + * Sets the alpha byte of a pixel. + * + * Constructs alpha to values from 0 to 255. + * @param alpha Alpha value from 0 (transparent) to 255 (opaque). + * @param rgb The 24bit rgb color to be combined with the alpga value. + * @return An ARBG calor value. + */ +static long setAlpha(int alpha, long rgb) +{ + if (alpha>255) + alpha=0; + else if (alpha<0) + alpha=0; + return (alpha<<24)|(rgb&0xFFFFFF); +} + +/** + * Sets the alpha byte of a pixel. + * + * Constricts alpha to values from 0 to 255. + * @param alpha Alpha value from 0.0f (transparent) to 1.0f (opaque). + * @param rgb The 24bit rgb color to be combined with the alpga value. + * @return An ARBG calor value. + */ +static long setAlpha(float alpha, long rgb) +{ + return setAlpha((int)(255.0f*alpha), rgb); +} + +/** + * Limits the values of a,r,g,b to values from 0 to 255 and puts them + * together into an 32 bit integer. + * + * @param a Alpha part, the first byte. + * @param r Red part, the second byte. + * @param g Green part, the third byte. + * @param b Blue part, the fourth byte. + * @return A ARBG value packed to an int. + */ +/* +static long packPixel(int a, int r, int g, int b) +{ + if (a<0) + a=0; + else if (a>255) + a=255; + + if (r<0) + r=0; + else if (r>255) + r=255; + + if (g<0) + g=0; + else if (g>255) + g=255; + + if (b<0) + b=0; + else if (b>255) + b=255; + + return (a<<24)|(r<<16)|(g<<8)|b; +} +*/ + +/** + * Returns the alpha component of an ARGB value. + * + * @param argb An ARGB color value. + * @return The alpha component, ranging from 0 to 255. + */ +/* +static int getAlpha(long argb) +{ + return (argb>>24)&0xFF; +} +*/ + +/** + * Returns the red component of an (A)RGB value. + * + * @param rgb An (A)RGB color value. + * @return The red component, ranging from 0 to 255. + */ +static int getRed(long rgb) +{ + return (rgb>>16)&0xFF; +} + + +/** + * Returns the green component of an (A)RGB value. + * + * @param rgb An (A)RGB color value. + * @return The green component, ranging from 0 to 255. + */ +static int getGreen(long rgb) +{ + return (rgb>>8)&0xFF; +} + +/** + * Returns the blue component of an (A)RGB value. + * + * @param rgb An (A)RGB color value. + * @return The blue component, ranging from 0 to 255. + */ +static int getBlue(long rgb) +{ + return (rgb)&0xFF; +} + +/** + * Returns a string representation of a CLAB value. + * + * @param clab The CLAB value. + * @param clabSize Size of the CLAB value. + * @return A string representation of the CLAB value. + */ +/* +static std::string clabToString(const CLAB &clab) +{ + std::string buff; + char nbuf[60]; + snprintf(nbuf, 59, "%5.3f, %5.3f, %5.3f", clab.L, clab.A, clab.B); + buff = nbuf; + return buff; +} +*/ + +//######################################################################## +//## C O L O R S I G N A T U R E (originally ColorSignature.java) +//######################################################################## + +/** + * Representation of a color signature. + *

+ * This class implements a clustering algorithm based on a modified kd-tree. + * The splitting rule is to simply divide the given interval into two equally + * sized subintervals. + * In the stageone(), approximate clusters are found by building + * up such a tree and stopping when an interval at a node has become smaller + * than the allowed cluster diameter, which is given by limits. + * At this point, clusters may be split in several nodes.
+ * Therefore, in stagetwo(), nodes that belong to several clusters + * are recombined by another k-d tree clustering on the prior cluster + * centroids. To guarantee a proper level of abstraction, clusters that contain + * less than 0.01% of the pixels of the entire sample are removed. Please + * refer to the file NOTICE to get links to further documentation. + * + * @author Gerald Friedland, Lars Knipping + * @version 1.02 + * + * Conversion to C++ by Bob Jamison + * + */ + +/** + * Stage one of clustering. + * @param points float[][] the input points in LAB space + * @param depth int used for recursion, start with 0 + * @param clusters ArrayList an Arraylist to store the clusters + * @param limits float[] the cluster diameters + */ +static void stageone(std::vector &points, + int depth, + std::vector< std::vector > &clusters, + float *limits) +{ + if (points.size() < 1) + return; + + int dims=3; + int curdim=depth%dims; + float min = 0.0f; + float max = 0.0f; + if (curdim == 0) + { + min=points[0].C; + max=points[0].C; + // find maximum and minimum + for (unsigned int i=1; ipoints[i].C) + min=points[i].C; + if (maxpoints[i].L) + min=points[i].L; + if (maxpoints[i].A) + min=points[i].A; + if (maxpoints[i].B) + min=points[i].B; + if (maxlimits[curdim]) { // Split according to Rubner-Rule + // split + float pivotvalue=((max-min)/2.0f)+min; + + std::vector smallerpoints; // allocate mem + std::vector biggerpoints; + + for (unsigned int i=0; i &points, + int depth, + std::vector< std::vector > &clusters, + float *limits, int total, float threshold) +{ + if (points.size() < 1) + return; + + int curdim=depth%3; // without cardinality + float min = 0.0f; + float max = 0.0f; + if (curdim == 0) + { + min=points[0].L; + max=points[0].L; + // find maximum and minimum + for (unsigned int i=1; ipoints[i].L) + min=points[i].L; + if (maxpoints[i].A) + min=points[i].A; + if (maxpoints[i].B) + min=points[i].B; + if (maxlimits[curdim]) { // Split according to Rubner-Rule + // split + float pivotvalue=((max-min)/2.0f)+min; + + std::vector smallerpoints; // allocate mem + std::vector biggerpoints; + + for (unsigned int i=0; i=threshold) { + CLAB point; + for (unsigned int i=0; i newCluster; + newCluster.push_back(point); + clusters.push_back(newCluster); + } + } +} + +/** + * Create a color signature for the given set of pixels. + * @param input float[][] a set of pixels in LAB space + * @param length int the number of pixels that should be processed from the input + * @param limits float[] the cluster diameters for each dimension + * @param threshold float the abstraction threshold + * @return float[][] a color siganture containing cluster centroids in LAB space + */ +static std::vector createSignature(std::vector &input, + float *limits, float threshold) +{ + std::vector< std::vector > clusters1; + std::vector< std::vector > clusters2; + + stageone(input, 0, clusters1, limits); + + std::vector centroids; + for (unsigned int i=0; i cluster = clusters1[i]; + CLAB centroid; // +1 for the cardinality + for (unsigned int k=0; k see paper by tomasi + + std::vector res; + for (unsigned int i=0 ; i=FOREGROUND_CONFIDENCE) + knownFg.push_back(rgbToClab(image[i])); + } + + bgSignature = createSignature(knownBg, limits, BACKGROUND_CONFIDENCE); + fgSignature = createSignature(knownFg, limits, BACKGROUND_CONFIDENCE); + + if (bgSignature.size() < 1) { + // segmentation impossible + return false; + } + + // classify using color signatures, + // classification cached in hashmap for drb and speedup purposes + for (int i=0; i=FOREGROUND_CONFIDENCE) { + cm[i]=CERTAIN_FOREGROUND_CONFIDENCE; + continue; + } + if (cm[i]>BACKGROUND_CONFIDENCE) { + bool isBackground=true; + std::map::iterator iter = hs.find(i); + Tupel tupel(0.0f, 0, 0.0f, 0); + if (iter == hs.end()) { + CLAB lab = rgbToClab(image[i]); + float minBg = sqrEuclidianDist(lab, bgSignature[0]); + int minIndex=0; + for (unsigned int j=1; j=UNKNOWN_REGION_CONFIDENCE) { + cm[i]=CERTAIN_FOREGROUND_CONFIDENCE; + } else { + cm[i]=CERTAIN_BACKGROUND_CONFIDENCE; + } + } + + keepOnlyLargeComponents(cm, cmSize, UNKNOWN_REGION_CONFIDENCE, sizeFactorToKeep); + fillColorRegions(cm, cmSize, image); + dilate(cm, imgWidth, imgHeight); + + segmentated=true; + return true; +} + + + +void SioxSegmentator::keepOnlyLargeComponents(float *cm, int cmSize, + float threshold, + double sizeFactorToKeep) +{ + int idx = 0; + for (int i=0 ; i labelSizes; + for (int i=0 ; i=threshold) { + regionCount=depthFirstSearch(cm, i, threshold, curlabel++); + labelSizes.push_back(regionCount); + } + + if (regionCount>maxregion) { + maxregion=regionCount; + maxblob=curlabel-1; + } + } + + for (int i=0 ; i pixelsToVisit; + int componentSize=0; + if (labelField[i]==-1 && cm[i]>=threshold) { // label #i + labelField[i] = curLabel; + ++componentSize; + pixelsToVisit.push_back(i); + } + while (pixelsToVisit.size() > 0) { + int pos=pixelsToVisit[pixelsToVisit.size()-1]; + pixelsToVisit.erase(pixelsToVisit.end()-1); + int x=pos%imgWidth; + int y=pos/imgWidth; + // check all four neighbours + int left = pos-1; + if (x-1>=0 && labelField[left]==-1 && cm[left]>=threshold) { + labelField[left]=curLabel; + ++componentSize; + pixelsToVisit.push_back(left); + } + int right = pos+1; + if (x+1=threshold) { + labelField[right]=curLabel; + ++componentSize; + pixelsToVisit.push_back(right); + } + int top = pos-imgWidth; + if (y-1>=0 && labelField[top]==-1 && cm[top]>=threshold) { + labelField[top]=curLabel; + ++componentSize; + pixelsToVisit.push_back(top); + } + int bottom = pos+imgWidth; + if (y+1=threshold) { + labelField[bottom]=curLabel; + ++componentSize; + pixelsToVisit.push_back(bottom); + } + } + return componentSize; +} + +void SioxSegmentator::subpixelRefine(int x, int y, int brushmode, + float threshold, float *cf, int brushsize) +{ + subpixelRefine(x-brushsize, y-brushsize, + 2*brushsize, 2*brushsize, + brushmode, threshold, cf); +} + + +bool SioxSegmentator::subpixelRefine(int xa, int ya, int dx, int dy, + int brushmode, + float threshold, float *cf) +{ + if (!segmentated) { + error("no segmentation yet"); + return false; + } + + int x0 = (xa > 0) ? xa : 0; + int y0 = (ya > 0) ? ya : 0; + + int xTo = (imgWidth - 1 < xa+dx ) ? imgWidth-1 : xa+dx; + int yTo = (imgHeight - 1 < ya+dy ) ? imgHeight-1 : ya+dy; + + for (int ey=y0; ey::iterator iter = hs.find(val); + if (iter != hs.end()) { + minDistBg=(float) sqrt((float)iter->second.minBgDist); + minDistFg=(float) sqrt((float)iter->second.minFgDist); + } else { + continue; + } + if (ADD_EDGE == brushmode) { // handle adder + if (cf[ey*imgWidth+ex]FOREGROUND_CONFIDENCE) { + // foreground, we want to take something away + float alpha; + if (minDistBg==0) { + alpha=CERTAIN_BACKGROUND_CONFIDENCE; + } else { + alpha=CERTAIN_FOREGROUND_CONFIDENCE- + (minDistFg/minDistBg < CERTAIN_FOREGROUND_CONFIDENCE) ? // more background -> >1 + minDistFg/minDistBg : CERTAIN_FOREGROUND_CONFIDENCE; + // bg = gf -> 1 + // more fg -> <1 + } + if (alpha pixelsToVisit; + for (int i=0; i 0) { + int pos=pixelsToVisit[pixelsToVisit.size()-1]; + pixelsToVisit.erase(pixelsToVisit.end()-1); + int x=pos%imgWidth; + int y=pos/imgWidth; + // check all four neighbours + int left = pos-1; + if (x-1>=0 && labelField[left]==-1 + && labcolordiff(image[left], origColor)<1.0) { + labelField[left]=curLabel; + cm[left]=CERTAIN_FOREGROUND_CONFIDENCE; + // ++componentSize; + pixelsToVisit.push_back(left); + } + int right = pos+1; + if (x+1=0 && labelField[top]==-1 + && labcolordiff(image[top], origColor)<1.0) { + labelField[top]=curLabel; + cm[top]=CERTAIN_FOREGROUND_CONFIDENCE; + // ++componentSize; + pixelsToVisit.push_back(top); + } + int bottom = pos+imgWidth; + if (y+1maxRegion) { + // maxRegion=componentSize; + //} + } +} + + + + + + + + + + + + + + +} //namespace siox +} //namespace org + + + diff --git a/src/trace/siox-segmentator.h b/src/trace/siox-segmentator.h new file mode 100644 index 000000000..4d92a9182 --- /dev/null +++ b/src/trace/siox-segmentator.h @@ -0,0 +1,396 @@ +#ifndef __SIOX_SEGMENTATOR_H__ +#define __SIOX_SEGMENTATOR_H__ +/* + Copyright 2005, 2006 by Gerald Friedland, Kristian Jantz and Lars Knipping + + Conversion to C++ for Inkscape by Bob Jamison + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include +#include + +namespace org +{ +namespace siox +{ + +/** + * Image segmentator based on + *SIOX: Simple Interactive Object Extraction. + *

+ * To segmentate an image one has to perform the following steps. + *

  1. Construct an instance of SioxSegmentator. + *
  2. Create a confidence matrix, where each entry marks its + * corresponding image pixel to belong to the foreground, to the + * background, or being of unknown type. + *
  3. Call segmentate on the image with the confidence + * matrix. This stores the result as new foreground confidence into + * the confidence matrix, with each entry being either + * zero (CERTAIN_BACKGROUND_CONFIDENCE) or one + * (CERTAIN_FOREGROUND_CONFIDENCE). + *
  4. Optionally call subpixelRefine to areas + * where pixels contain both foreground and background (e.g. + * object borders or highly detailed features like flowing hairs). + * The pixel are then assigned confidence values bwetween zero and + * one to give them a measure of "foregroundness". + * This step may be repeated as often as needed. + *
+ *

+ * For algorithm documentation refer to + * G. Friedland, K. Jantz, L. Knipping, R. Rojas: + * Image Segmentation by Uniform Color Clustering + * -- Approach and Benchmark Results, + * Technical Report B-05-07, + * Department of Computer Science, Freie Universitaet Berlin, June 2005.
+ *

+ * See http://www.siox.org for more information.
+ *

+ * Algorithm idea by Gerald Friedland. + * + * @author Gerald Friedland, Kristian Jantz, Lars Knipping + * @version 1.12 + */ + +/** + * Helper class for storing the minimum distances to a cluster centroid + * in background and foreground and the index to the centroids in each + * signature for a given color. + */ +class Tupel { +public: + + Tupel() + { + minBgDist = 0.0f; + indexMinBg = 0; + minFgDist = 0.0f; + indexMinFg = 0; + } + Tupel(float minBgDistArg, long indexMinBgArg, + float minFgDistArg, long indexMinFgArg) + { + minBgDist = minBgDistArg; + indexMinBg = indexMinBgArg; + minFgDist = minFgDistArg; + indexMinFg = indexMinFgArg; + } + Tupel(const Tupel &other) + { + minBgDist = other.minBgDist; + indexMinBg = other.indexMinBg; + minFgDist = other.minFgDist; + indexMinFg = other.indexMinFg; + } + Tupel &operator=(const Tupel &other) + { + minBgDist = other.minBgDist; + indexMinBg = other.indexMinBg; + minFgDist = other.minFgDist; + indexMinFg = other.indexMinFg; + return *this; + } + virtual ~Tupel() + {} + + float minBgDist; + long indexMinBg; + float minFgDist; + long indexMinFg; + + }; + + +class CLAB +{ +public: + CLAB() + { + C = L = A = B = 0.0f; + } + CLAB(float lArg, float aArg, float bArg) + { + C = 0.0f; + L = lArg; + A = aArg; + B = bArg; + } + CLAB(const CLAB &other) + { + C = other.C; + L = other.L; + A = other.A; + B = other.B; + } + CLAB &operator=(const CLAB &other) + { + C = other.C; + L = other.L; + A = other.A; + B = other.B; + return *this; + } + virtual ~CLAB() + {} + + float C; + float L; + float A; + float B; +}; + + +class SioxSegmentator +{ +public: + + /** Confidence corresponding to a certain foreground region (equals one). */ + static const float CERTAIN_FOREGROUND_CONFIDENCE=1.0f; + + /** Confidence for a region likely being foreground.*/ + static const float FOREGROUND_CONFIDENCE=0.8f; + + /** Confidence for foreground or background type being equally likely.*/ + static const float UNKNOWN_REGION_CONFIDENCE=0.5f; + + /** Confidence for a region likely being background.*/ + static const float BACKGROUND_CONFIDENCE=0.1f; + + /** Confidence corresponding to a certain background reagion (equals zero). */ + static const float CERTAIN_BACKGROUND_CONFIDENCE=0.0f; + + + /** + * Constructs a SioxSegmentator Object to be used for image segmentation. + * + * @param w X resolution of the image to be segmentated. + * @param h Y resolution of the image to be segmentated. + * @param limits Size of the cluster on LAB axises. + * If null, the default value {0.64f,1.28f,2.56f} + * is used. + */ + SioxSegmentator(int w, int h, float *limitsArg, int limitsSize); + + /** + * Destructor + */ + virtual ~SioxSegmentator(); + + + /** + * Segmentates the given image with information from the confidence + * matrix. + *

+ * The confidence entries of BACKGROUND_CONFIDENCE or less + * are mark known background pixel for the segmentation, those + * of at least FOREGROUND_CONFIDENCE mark known + * foreground pixel for the segmentation. Any other entry is treated + * as region of unknown affiliation. + *

+ * As result, each pixel is classified either as foregroound or + * background, stored back into its cm entry as confidence + * CERTAIN_FOREGROUND_CONFIDENCE or + * CERTAIN_BACKGROUND_CONFIDENCE. + * + * @param image Pixel data of the image to be segmentated. + * Every integer represents one ARGB-value. + * @param imageSize number of values in image + * @param cm Confidence matrix specifying the probability of an image + * belonging to the foreground before and after the segmentation. + * @param smoothness Number of smoothing steps in the post processing. + * @param sizeFactorToKeep Segmentation retains the largest connected + * foreground component plus any component with size at least + * sizeOfLargestComponent/sizeFactorToKeep. + * @return true if the segmentation algorithm succeeded, + * false if segmentation is impossible + */ + bool segmentate(long *image, int imageSize, + float *cm, int cmSize, + int smoothness, double sizeFactorToKeep); + + /** + * Clears given confidence matrix except entries for the largest connected + * component and every component with + * size*sizeFactorToKeep >= sizeOfLargestComponent. + * + * @param cm Confidence matrix to be analysed + * @param threshold Pixel visibility threshold. + * Exactly those cm entries larger than threshold are considered + * to be a "visible" foreground pixel. + * @param sizeFactorToKeep This method keeps the largest connected + * component plus any component with size at least + * sizeOfLargestComponent/sizeFactorToKeep. + */ + void keepOnlyLargeComponents(float *cm, int cmSize, + float threshold, + double sizeFactorToKeep); + + /** + * Depth first search pixels in a foreground component. + * + * @param cm confidence matrix to be searched. + * @param i starting position as index to confidence matrix. + * @param threshold defines the minimum value at which a pixel is + * considered foreground. + * @param curlabel label no of component. + * @return size in pixel of the component found. + */ + int depthFirstSearch(float *cm, int i, float threshold, int curLabel); + + /** + * Refines the classification stored in the confidence matrix by modifying + * the confidences for regions which have characteristics to both + * foreground and background if they fall into the specified square. + *

+ * The can be used in displaying the image by assigning the alpha values + * of the pixels according to the confidence entries. + *

+ * In the algorithm descriptions and examples GUIs this step is referrered + * to as Detail Refinement (Brush). + * + * @param x Horizontal coordinate of the squares center. + * @param y Vertical coordinate of the squares center. + * @param brushmode Mode of the refinement applied, ADD_EDGE + * or SUB_EDGE. Add mode only modifies pixels + * formerly classified as background, sub mode only those + * formerly classified as foreground. + * @param threshold Threshold for the add and sub refinement, deciding + * at the confidence level to stop at. + * @param cf The confidence matrix to modify, generated by + * segmentate, possibly already refined by privious + * calls to subpixelRefine. + * @param brushsize Halfed diameter of the square shaped brush. + * + * @see #segmentate + */ + void SioxSegmentator::subpixelRefine(int x, int y, int brushmode, + float threshold, float *cf, int brushsize); + + /** + * Refines the classification stored in the confidence matrix by modifying + * the confidences for regions which have characteristics to both + * foreground and background if they fall into the specified area. + *

+ * The can be used in displaying the image by assigning the alpha values + * of the pixels according to the confidence entries. + *

+ * In the algorithm descriptions and examples GUIs this step is referrered + * to as Detail Refinement (Brush). + * + * @param area Area in which the reworking of the segmentation is + * applied to. + * @param brushmode Mode of the refinement applied, ADD_EDGE + * or SUB_EDGE. Add mode only modifies pixels + * formerly classified as background, sub mode only those + * formerly classified as foreground. + * @param threshold Threshold for the add and sub refinement, deciding + * at the confidence level to stop at. + * @param cf The confidence matrix to modify, generated by + * segmentate, possibly already refined by privious + * calls to subpixelRefine. + * + * @see #segmentate + */ + bool SioxSegmentator::subpixelRefine(int xa, int ya, int dx, int dy, + int brushmode, + float threshold, float *cf); + /** + * A region growing algorithms used to fill up the confidence matrix + * with CERTAIN_FOREGROUND_CONFIDENCE for corresponding + * areas of equal colors. + *

+ * Basically, the method works like the Magic Wand with a + * tolerance threshold of zero. + * + * @param cm confidence matrix to be searched + * @param image image to be searched + */ + void fillColorRegions(float *cm, int cmSize, long *image); + +private: + + /** + * Prevent this from being used + */ + SioxSegmentator(); + + /** error logging **/ + void error(char *format, ...); + + /** trace logging **/ + void trace(char *format, ...); + + typedef enum + { + ADD_EDGE, /** Add mode for the subpixel refinement. */ + SUB_EDGE /** Subtract mode for the subpixel refinement. */ + } BrushMode; + + // instance fields: + + /** Horizontal resolution of the image to be segmentated. */ + int imgWidth; + + /** Vertical resolution of the image to be segmentated. */ + int imgHeight; + + /** Stores component label (index) by pixel it belongs to. */ + int *labelField; + + /** + * LAB color values of pixels that are definitly known background. + * Entries are of form {l,a,b}. + */ + std::vector knownBg; + + /** + * LAB color values of pixels that are definitly known foreground. + * Entries are of form {l,a,b}. + */ + std::vector knownFg; + + /** Holds background signature (a characteristic subset of the bg.) */ + std::vector bgSignature; + + /** Holds foreground signature (a characteristic subset of the fg).*/ + std::vector fgSignature; + + /** Size of cluster on lab axis. */ + float *limits; + + /** Maximum distance of two lab values. */ + float clusterSize; + + /** + * Stores Tupels for fast access to nearest background/foreground pixels. + */ + std::map hs; + + /** Size of the biggest blob.*/ + int regionCount; + + /** Copy of the original image, needed for detail refinement. */ + long *origImage; + long origImageSize; + + /** A flag that stores if the segmentation algorithm has already ran.*/ + bool segmentated; + +}; + +} //namespace siox +} //namespace org + +#endif /* __SIOX_SEGMENTATOR_H__ */ +