Code

Add siox
authorishmal <ishmal@users.sourceforge.net>
Fri, 24 Mar 2006 14:40:42 +0000 (14:40 +0000)
committerishmal <ishmal@users.sourceforge.net>
Fri, 24 Mar 2006 14:40:42 +0000 (14:40 +0000)
src/trace/Makefile_insert
src/trace/siox-segmentator.cpp [new file with mode: 0644]
src/trace/siox-segmentator.h [new file with mode: 0644]

index 92686be0bdda604d8379901e96d8ebba027c5377..6a2100207ca4622eaa05c6d4f1f3a8486bc50ef6 100644 (file)
@@ -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 (file)
index 0000000..31d99ca
--- /dev/null
@@ -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 <string>
+
+#include <stdarg.h> //for error() and trace()
+#include <math.h>   //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<long, CLAB> 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; y<yres; y++) {
+        for (int x=0; x<xres-1; x++) {
+            int idx=(y*xres)+x;
+            if (cm[idx+1]>cm[idx])
+                cm[idx]=cm[idx+1];
+            }
+        }
+    for (int y=0; y<yres; y++) {
+            for (int x=xres-1; x>=1; x--) {
+            int idx=(y*xres)+x;
+            if (cm[idx-1]>cm[idx])
+                cm[idx]=cm[idx-1];
+        }
+    }
+    for (int y=0; y<yres-1; y++) {
+        for (int x=0; x<xres; x++) {
+        int idx=(y*xres)+x;
+        if (cm[((y+1)*xres)+x] > cm[idx])
+            cm[idx]=cm[((y+1)*xres)+x];
+        }
+    }
+    for (int y=yres-1; y>=1; y--) {
+        for (int x=0; x<xres; x++) {
+        int idx=(y*xres)+x;
+        if (cm[((y-1)*xres)+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<yres; y++) {
+        for (int x=0; x<xres-1; x++) {
+            int idx=(y*xres)+x;
+            if (cm[idx+1] < cm[idx])
+                cm[idx]=cm[idx+1];
+        }
+    }
+    for (int y=0; y<yres; y++) {
+        for (int x=xres-1; x>=1; x--) {
+            int idx=(y*xres)+x;
+            if (cm[idx-1] < cm[idx])
+                cm[idx]=cm[idx-1];
+        }
+    }
+    for (int y=0; y<yres-1; y++) {
+        for (int x=0; x<xres; x++) {
+            int idx=(y*xres)+x;
+            if (cm[((y+1)*xres)+x] < cm[idx])
+                cm[idx]=cm[((y+1)*xres)+x];
+        }
+    }
+    for (int y=yres-1; y>=1; y--) {
+        for (int x=0; x<xres; x++) {
+            int idx=(y*xres)+x;
+            if (cm[((y-1)*xres)+x] < cm[idx])
+                cm[idx]=cm[((y-1)*xres)+x];
+        }
+    }
+}
+
+/**
+ * Normalizes the matrix to values to [0..1].
+ *
+ * @param cm The matrix to be normalized.
+ */
+static void normalizeMatrix(float *cm, int cmSize)
+{
+    float max=0.0f;
+    for (int i=0; i<cmSize; i++) {
+        if (max<cm[i])
+            max=cm[i];
+    }
+    if (max<=0.0)
+        return;
+    else if (max==1.00)
+        return;
+
+    float alpha=1.00f/max;
+    premultiplyMatrix(alpha, cm, cmSize);
+}
+
+/**
+ * Multiplies matrix with the given scalar.
+ *
+ * @param alpha The scalar value.
+ * @param cm The matrix of values be multiplied with alpha.
+ * @param cmSize The matrix length.
+ */
+static void premultiplyMatrix(float alpha, float *cm, int cmSize)
+{
+    for (int i=0; i<cmSize; i++)
+        cm[i]=alpha*cm[i];
+}
+
+/**
+ * Blurs confidence matrix with a given symmetrically weighted kernel.
+ * <P>
+ * 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<yres; y++) {
+        for (int x=0; x<xres-2; x++) {
+            int idx=(y*xres)+x;
+            cm[idx]=f1*cm[idx]+f2*cm[idx+1]+f3*cm[idx+2];
+        }
+    }
+    for (int y=0; y<yres; y++) {
+        for (int x=xres-1; x>=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<yres-2; y++) {
+        for (int x=0; x<xres; x++) {
+            int idx=(y*xres)+x;
+            cm[idx]=f1*cm[idx]+f2*cm[((y+1)*xres)+x]+f3*cm[((y+2)*xres)+x];
+        }
+    }
+    for (int y=yres-1; y>=2; y--) {
+        for (int x=0; x<xres; x++) {
+            int idx=(y*xres)+x;
+            cm[idx]=f3*cm[((y-2)*xres)+x]+f2*cm[((y-1)*xres)+x]+f1*cm[idx];
+        }
+    }
+}
+
+/**
+ * Squared Euclidian distance of p and q.
+ * <P>
+ * 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<pSize; i++)
+        sum+=(p[i]-q[i])*(p[i]-q[i]);
+    return sum;
+}
+
+/**
+ * Squared Euclidian distance of p and q.
+ * <P>
+ * 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.
+ * <P>
+ * 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.
+ * <P>
+ * The conversion used is decribed at
+ * <a href="http://www.easyrgb.com/math.php?MATH=M7#text7">CLAB Conversion</a>
+ * 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<long, CLAB>::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.
+ * <P>
+ * 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.
+ * <br><br>
+ * 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 <code>stageone()</code>, 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 <code>limits</code>.
+ * At this point, clusters may be split in several nodes.<br>
+ * Therefore, in <code>stagetwo()</code>, 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<CLAB> &points,
+                     int depth,
+                     std::vector< std::vector<CLAB> > &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; i<points.size(); i++) {
+            if (min>points[i].C)
+                min=points[i].C;
+            if (max<points[i].C)
+                max=points[i].C;
+            }
+        }
+    else if (curdim == 1)
+        {
+        min=points[0].L;
+        max=points[0].L;
+        // find maximum and minimum
+        for (unsigned int i=1; i<points.size(); i++) {
+            if (min>points[i].L)
+                min=points[i].L;
+            if (max<points[i].L)
+                max=points[i].L;
+            }
+        }
+    else if (curdim == 2)
+        {
+        min=points[0].A;
+        max=points[0].A;
+        // find maximum and minimum
+        for (unsigned int i=1; i<points.size(); i++) {
+            if (min>points[i].A)
+                min=points[i].A;
+            if (max<points[i].A)
+                max=points[i].A;
+            }
+        }
+    else if (curdim == 3)
+        {
+        min=points[0].B;
+        max=points[0].B;
+        // find maximum and minimum
+        for (unsigned int i=1; i<points.size(); i++) {
+            if (min>points[i].B)
+                min=points[i].B;
+            if (max<points[i].B)
+                max=points[i].B;
+            }
+        }
+
+    if (max-min>limits[curdim]) { // Split according to Rubner-Rule
+        // split
+        float pivotvalue=((max-min)/2.0f)+min;
+
+        std::vector<CLAB> smallerpoints; // allocate mem
+        std::vector<CLAB> biggerpoints;
+
+        for (unsigned int i=0; i<points.size(); i++) { // do actual split
+            float v = 0.0f;
+            if (curdim==0)
+                v = points[i].C;
+            else if (curdim==1)
+                v = points[i].L;
+            else if (curdim==2)
+                v = points[i].A;
+            else if (curdim==3)
+                v = points[i].B;
+            if (v <= pivotvalue) {
+                smallerpoints.push_back(points[i]);
+            } else {
+                biggerpoints.push_back(points[i]);
+            }
+        } // create subtrees
+        stageone(smallerpoints, depth+1, clusters, limits);
+        stageone(biggerpoints,  depth+1, clusters, limits);
+    } else { // create leave
+        clusters.push_back(points);
+    }
+}
+
+/**
+ * Stage two 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
+ * @param total int the total number of points as given to stageone
+ * @param threshold should be 0.01 - abstraction threshold
+ */
+static void stagetwo(std::vector<CLAB> &points,
+                     int depth,
+                     std::vector< std::vector<CLAB> > &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; i<points.size(); i++) {
+            if (min>points[i].L)
+                min=points[i].L;
+            if (max<points[i].L)
+                max=points[i].L;
+            }
+        }
+    else if (curdim == 1)
+        {
+        min=points[0].A;
+        max=points[0].A;
+        // find maximum and minimum
+        for (unsigned int i=1; i<points.size(); i++) {
+            if (min>points[i].A)
+                min=points[i].A;
+            if (max<points[i].A)
+                max=points[i].A;
+            }
+        }
+    else if (curdim == 2)
+        {
+        min=points[0].B;
+        max=points[0].B;
+        // find maximum and minimum
+        for (unsigned int i=1; i<points.size(); i++) {
+            if (min>points[i].B)
+                min=points[i].B;
+            if (max<points[i].B)
+                max=points[i].B;
+            }
+        }
+
+
+    if (max-min>limits[curdim]) { // Split according to Rubner-Rule
+        // split
+        float pivotvalue=((max-min)/2.0f)+min;
+
+        std::vector<CLAB> smallerpoints; // allocate mem
+        std::vector<CLAB> biggerpoints;
+
+        for (unsigned int i=0; i<points.size(); i++) { // do actual split
+            float v = 0.0f;
+           if (curdim==0)
+               v = points[i].L;
+           else if (curdim==1)
+               v = points[i].A;
+           else if (curdim==2)
+               v = points[i].B;
+
+            if (v <= pivotvalue) {
+                smallerpoints.push_back(points[i]);
+            } else {
+                biggerpoints.push_back(points[i]);
+            }
+        } // create subtrees
+        stagetwo(smallerpoints, depth+1, clusters, limits, total, threshold);
+        stagetwo(biggerpoints,  depth+1, clusters, limits, total, threshold);
+    } else { // create leave
+        float sum=0.0;
+        for (unsigned int i=0; i<points.size(); i++) {
+            sum+=points[i].B;
+        }
+        if (((sum*100.0)/total)>=threshold) {
+            CLAB point;
+            for (unsigned int i=0; i<points.size(); i++) {
+                point.C += points[i].C;
+                point.L += points[i].L;
+                point.A += points[i].A;
+                point.B += points[i].B;
+            }
+            point.C /= points.size();
+            point.L /= points.size();
+            point.A /= points.size();
+            point.B /= points.size();
+
+            std::vector<CLAB> 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<CLAB> createSignature(std::vector<CLAB> &input,
+                                         float *limits, float threshold)
+{
+    std::vector< std::vector<CLAB> > clusters1;
+    std::vector< std::vector<CLAB> > clusters2;
+
+    stageone(input, 0, clusters1, limits);
+
+    std::vector<CLAB> centroids;
+    for (unsigned int i=0; i<clusters1.size(); i++) {
+        std::vector<CLAB> cluster = clusters1[i];
+        CLAB centroid; // +1 for the cardinality
+        for (unsigned int k=0; k<cluster.size(); k++) {
+            centroid.L += cluster[k].L;
+            centroid.A += cluster[k].A;
+            centroid.B += cluster[k].B;
+        }
+        centroid.C =  cluster.size();
+        centroid.L /= cluster.size();
+        centroid.A /= cluster.size();
+        centroid.B /= cluster.size();
+
+        centroids.push_back(centroid);
+    }
+    stagetwo(centroids, 0, clusters2, limits, input.size(), threshold); // 0.1 -> see paper by tomasi
+
+    std::vector<CLAB> res;
+    for (unsigned int i=0 ; i<clusters2.size() ; i++)
+        for (unsigned int j=0 ; j<clusters2[i].size() ; j++)
+            res.push_back(clusters2[i][j]);
+
+    return res;
+}
+
+
+
+//########################################################################
+//##  S I O X  S E G M E N T A T O R    (originally SioxSegmentator.java)
+//########################################################################
+
+//### NOTE: Doxygen comments are in siox-segmentator.h
+
+
+SioxSegmentator::SioxSegmentator(int w, int h, float *limitsArg, int limitsSize)
+{
+
+    imgWidth   = w;
+    imgHeight  = h;
+    labelField = new int[imgWidth*imgHeight];
+
+    if (!limitsArg) {
+        limits = new float[3];
+        limits[0] = 0.64f;
+        limits[1] = 1.28f;
+        limits[2] = 2.56f;
+    } else {
+        limits = new float[limitsSize];
+        for (int i=0 ; i<limitsSize ; i++)
+            limits[i] = limitsArg[i];
+    }
+
+    float negLimits[3];
+    negLimits[0] = -limits[0];
+    negLimits[1] = -limits[1];
+    negLimits[2] = -limits[2];
+    clusterSize = sqrEuclidianDist(limits, 3, negLimits);
+
+    segmentated=false;
+
+    origImage = NULL;
+}
+
+SioxSegmentator::~SioxSegmentator()
+{
+    delete labelField;
+    delete limits;
+    delete origImage;
+}
+
+
+/**
+ * Error logging
+ */
+void SioxSegmentator::error(char *fmt, ...)
+{
+    va_list args;
+    fprintf(stderr, "SioxSegmentator error:");
+    va_start(args, fmt);
+    vfprintf(stderr, fmt, args);
+    va_end(args) ;
+    fprintf(stderr, "\n");
+}
+
+/**
+ * Trace logging
+ */
+void SioxSegmentator::trace(char *fmt, ...)
+{
+    va_list args;
+    fprintf(stderr, "SioxSegmentator:");
+    va_start(args, fmt);
+    vfprintf(stderr, fmt, args);
+    va_end(args) ;
+    fprintf(stderr, "\n");
+}
+
+
+
+bool SioxSegmentator::segmentate(long *image, int imageSize,
+                                 float *cm, int cmSize,
+                                 int smoothness, double sizeFactorToKeep)
+{
+    segmentated=false;
+
+    hs.clear();
+
+    // save image for drb
+    origImage=new long[imageSize];
+    for (int i=0 ; i<imageSize ; i++)
+        origImage[i] = image[i];
+
+    // create color signatures
+    for (int i=0; i<cmSize; i++) {
+        if (cm[i]<=BACKGROUND_CONFIDENCE)
+            knownBg.push_back(rgbToClab(image[i]));
+        else if (cm[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<cmSize; i++) {
+        if (cm[i]>=FOREGROUND_CONFIDENCE) {
+            cm[i]=CERTAIN_FOREGROUND_CONFIDENCE;
+            continue;
+        }
+        if (cm[i]>BACKGROUND_CONFIDENCE) {
+            bool isBackground=true;
+            std::map<long, Tupel>::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<bgSignature.size(); j++) {
+                    float d = sqrEuclidianDist(lab, bgSignature[j]);
+                    if (d<minBg) {
+                        minBg=d;
+                        minIndex=j;
+                    }
+                }
+                tupel.minBgDist=minBg;
+                tupel.indexMinBg=minIndex;
+                float minFg = 1.0e6f;
+                minIndex=-1;
+                for (unsigned int j=0 ; j<fgSignature.size() ; j++) {
+                    float d = sqrEuclidianDist(lab, fgSignature[j]);
+                    if (d<minFg) {
+                        minFg=d;
+                        minIndex=j;
+                    }
+                }
+                tupel.minFgDist=minFg;
+                tupel.indexMinFg=minIndex;
+                if (fgSignature.size()==0) {
+                    isBackground=(minBg<=clusterSize);
+                    // remove next line to force behaviour of old algorithm
+                    error("foreground signature does not exist");
+                    return false;
+                } else {
+                    isBackground=minBg<minFg;
+                }
+                hs[image[i]] = tupel;
+            } else {
+                isBackground=tupel.minBgDist<=tupel.minFgDist;
+            }
+            if (isBackground) {
+                cm[i]=CERTAIN_BACKGROUND_CONFIDENCE;
+            } else {
+                cm[i]=CERTAIN_FOREGROUND_CONFIDENCE;
+            }
+        } else {
+            cm[i]=CERTAIN_BACKGROUND_CONFIDENCE;
+        }
+    }
+
+    // postprocessing
+    smoothcm(cm, imgWidth, imgHeight, 0.33f, 0.33f, 0.33f); // average
+    normalizeMatrix(cm, cmSize);
+    erode(cm, imgWidth, imgHeight);
+    keepOnlyLargeComponents(cm, cmSize, UNKNOWN_REGION_CONFIDENCE, sizeFactorToKeep);
+
+    for (int i=0; i<smoothness; i++) {
+        smoothcm(cm, imgWidth, imgHeight, 0.33f, 0.33f, 0.33f); // average
+    }
+
+    normalizeMatrix(cm, cmSize);
+
+    for (int i=0; i<cmSize; i++) {
+        if (cm[i]>=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<imgHeight ; i++)
+        for (int j=0 ; j<imgWidth ; j++)
+            labelField[idx++] = -1;
+
+    int curlabel = 0;
+    int maxregion= 0;
+    int maxblob  = 0;
+
+    // slow but easy to understand:
+    std::vector<int> labelSizes;
+    for (int i=0 ; i<cmSize ; i++) {
+        regionCount=0;
+        if (labelField[i]==-1 && cm[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<cmSize ; i++) {
+        if (labelField[i]!=-1) {
+            // remove if the component is to small
+            if (labelSizes[labelField[i]]*sizeFactorToKeep < maxregion)
+                cm[i]=CERTAIN_BACKGROUND_CONFIDENCE;
+
+            // add maxblob always to foreground
+            if (labelField[i]==maxblob)
+                cm[i]=CERTAIN_FOREGROUND_CONFIDENCE;
+        }
+    }
+}
+
+
+
+int SioxSegmentator::depthFirstSearch(float *cm, int i, float threshold, int curLabel)
+{
+    // stores positions of labeled pixels, where the neighbours
+    // should still be checked for processing:
+    std::vector<int> 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<imgWidth && labelField[right]==-1 && cm[right]>=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<imgHeight && labelField[bottom]==-1
+            && cm[bottom]>=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<yTo; ++ey) {
+        for (int ex=x0; ex<xTo; ++ex) {
+            /*  we are using a rect, not necessary
+            if (!area.contains(ex, ey)) {
+                continue;
+            }
+            */
+            long val=origImage[ey*imgWidth+ex];
+            long orig=val;
+            float minDistBg = 0.0f;
+            float minDistFg = 0.0f;
+            std::map<long, Tupel>::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) { // postprocessing wins
+                  float alpha;
+                  if (minDistFg==0) {
+                      alpha=CERTAIN_FOREGROUND_CONFIDENCE;
+                  } else {
+                      alpha = (minDistBg/minDistFg < CERTAIN_FOREGROUND_CONFIDENCE) ?
+                          minDistBg/minDistFg : CERTAIN_FOREGROUND_CONFIDENCE;
+                  }
+                  if (alpha<threshold) { // background with certain confidence decided by user.
+                      alpha=CERTAIN_BACKGROUND_CONFIDENCE;
+                  }
+                  val = setAlpha(alpha, orig);
+                  cf[ey*imgWidth+ex]=alpha;
+                }
+            } else if (SUB_EDGE == brushmode) { // handle substractor
+                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<threshold) { // background with certain confidence decided by user
+                        alpha=CERTAIN_BACKGROUND_CONFIDENCE;
+                    }
+                    val = setAlpha(alpha, orig);
+                    cf[ey*imgWidth+ex]=alpha;
+                }
+            } else {
+                error("unknown brush mode: %d", brushmode);
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+
+
+void SioxSegmentator::fillColorRegions(float *cm, int cmSize, long *image)
+{
+    int idx = 0;
+    for (int i=0 ; i<imgHeight ; i++)
+        for (int j=0 ; i<imgWidth ; j++)
+            labelField[idx++] = -1;
+
+    //int maxRegion=0; // unused now
+    std::vector<int> pixelsToVisit;
+    for (int i=0; i<cmSize; i++) { // for all pixels
+        if (labelField[i]!=-1 || cm[i]<UNKNOWN_REGION_CONFIDENCE) {
+            continue; // already visited or bg
+        }
+        int origColor=image[i];
+        int curLabel=i+1;
+        labelField[i]=curLabel;
+        cm[i]=CERTAIN_FOREGROUND_CONFIDENCE;
+        // int componentSize = 1;
+        pixelsToVisit.push_back(i);
+        // depth first search to fill region
+        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
+                && 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<imgWidth && labelField[right]==-1
+                && labcolordiff(image[right], origColor)<1.0) {
+                labelField[right]=curLabel;
+                cm[right]=CERTAIN_FOREGROUND_CONFIDENCE;
+                // ++componentSize;
+                pixelsToVisit.push_back(right);
+            }
+            int top = pos-imgWidth;
+            if (y-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+1<imgHeight && labelField[bottom]==-1
+                && labcolordiff(image[bottom], origColor)<1.0) {
+                labelField[bottom]=curLabel;
+                cm[bottom]=CERTAIN_FOREGROUND_CONFIDENCE;
+                // ++componentSize;
+                pixelsToVisit.push_back(bottom);
+            }
+        }
+        //if (componentSize>maxRegion) {
+        //    maxRegion=componentSize;
+        //}
+    }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+} //namespace siox
+} //namespace org
+
+
+
diff --git a/src/trace/siox-segmentator.h b/src/trace/siox-segmentator.h
new file mode 100644 (file)
index 0000000..4d92a91
--- /dev/null
@@ -0,0 +1,396 @@
+#ifndef __SIOX_SEGMENTATOR_H__\r
+#define __SIOX_SEGMENTATOR_H__\r
+/*\r
+   Copyright 2005, 2006 by Gerald Friedland, Kristian Jantz and Lars Knipping\r
+\r
+   Conversion to C++ for Inkscape by Bob Jamison\r
+\r
+   Licensed under the Apache License, Version 2.0 (the "License");\r
+   you may not use this file except in compliance with the License.\r
+   You may obtain a copy of the License at\r
+\r
+   http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+ */\r
+\r
+#include <map>\r
+#include <vector>\r
+\r
+namespace org\r
+{\r
+namespace siox\r
+{\r
+\r
+/**\r
+ * Image segmentator based on\r
+ *<em>SIOX: Simple Interactive Object Extraction</em>.\r
+ * <P>\r
+ * To segmentate an image one has to perform the following steps.\r
+ * <OL><LI>Construct an instance of <code>SioxSegmentator</code>.\r
+ * </LI><LI>Create a confidence matrix, where each entry marks its\r
+ *      corresponding image pixel to belong to the foreground, to the\r
+ *      background, or being of unknown type.\r
+ * </LI><LI>Call <code>segmentate</code> on the image with the confidence\r
+ *       matrix. This stores the result as new foreground confidence into\r
+ *       the confidence matrix, with each entry being either\r
+ *       zero (<code>CERTAIN_BACKGROUND_CONFIDENCE</code>) or one\r
+ *       (<code>CERTAIN_FOREGROUND_CONFIDENCE</code>).\r
+ * </LI><LI>Optionally call <code>subpixelRefine</code> to areas\r
+ *      where pixels contain both foreground and background (e.g.\r
+ *      object borders or highly detailed features like flowing hairs).\r
+ *      The pixel are then assigned confidence values bwetween zero and\r
+ *      one to give them a measure of "foregroundness".\r
+ *      This step may be repeated as often as needed.\r
+ * </LI></OL>\r
+ * <P>\r
+ * For algorithm documentation refer to\r
+ * G. Friedland, K. Jantz, L. Knipping, R. Rojas:<i>\r
+ * Image Segmentation by Uniform Color Clustering\r
+ *  -- Approach and Benchmark Results</i>,\r
+ * <A HREF="http://www.inf.fu-berlin.de/inst/pubs/tr-b-05-07.pdf">Technical Report B-05-07</A>,\r
+ * Department of Computer Science, Freie Universitaet Berlin, June 2005.<br>\r
+ * <P>\r
+ * See <A HREF="http://www.siox.org/" target="_new">http://www.siox.org</A> for more information.<br>\r
+ * <P>\r
+ * Algorithm idea by Gerald Friedland.\r
+ *\r
+ * @author Gerald Friedland, Kristian Jantz, Lars Knipping\r
+ * @version 1.12\r
+ */\r
+\r
+/**\r
+ * Helper class for storing the minimum distances to a cluster centroid\r
+ * in background and foreground and the index to the centroids in each\r
+ * signature for a given color.\r
+ */\r
+class Tupel {\r
+public:\r
+\r
+    Tupel()\r
+        {\r
+        minBgDist  = 0.0f;\r
+        indexMinBg = 0;\r
+        minFgDist  = 0.0f;\r
+        indexMinFg = 0;\r
+        }\r
+    Tupel(float minBgDistArg, long indexMinBgArg,\r
+          float minFgDistArg, long indexMinFgArg)\r
+        {\r
+       minBgDist  = minBgDistArg;\r
+       indexMinBg = indexMinBgArg;\r
+       minFgDist  = minFgDistArg;\r
+       indexMinFg = indexMinFgArg;\r
+        }\r
+    Tupel(const Tupel &other)\r
+        {\r
+       minBgDist  = other.minBgDist;\r
+       indexMinBg = other.indexMinBg;\r
+       minFgDist  = other.minFgDist;\r
+       indexMinFg = other.indexMinFg;\r
+        }\r
+    Tupel &operator=(const Tupel &other)\r
+        {\r
+       minBgDist  = other.minBgDist;\r
+       indexMinBg = other.indexMinBg;\r
+       minFgDist  = other.minFgDist;\r
+       indexMinFg = other.indexMinFg;\r
+       return *this;\r
+        }\r
+    virtual ~Tupel()\r
+        {}\r
+\r
+    float minBgDist;\r
+    long  indexMinBg;\r
+    float minFgDist;\r
+    long  indexMinFg;\r
+\r
+ };\r
+\r
+\r
+class CLAB\r
+{\r
+public:\r
+    CLAB()\r
+        {\r
+        C = L = A = B = 0.0f;\r
+        }\r
+    CLAB(float lArg, float aArg, float bArg)\r
+        {\r
+        C = 0.0f;\r
+        L = lArg;\r
+        A = aArg;\r
+        B = bArg;\r
+        }\r
+    CLAB(const CLAB &other)\r
+        {\r
+        C = other.C;\r
+        L = other.L;\r
+        A = other.A;\r
+        B = other.B;\r
+        }\r
+    CLAB &operator=(const CLAB &other)\r
+        {\r
+        C = other.C;\r
+        L = other.L;\r
+        A = other.A;\r
+        B = other.B;\r
+        return *this;\r
+        }\r
+    virtual ~CLAB()\r
+        {}\r
+\r
+    float C;\r
+    float L;\r
+    float A;\r
+    float B;\r
+};\r
+\r
+\r
+class SioxSegmentator\r
+{\r
+public:\r
+\r
+    /** Confidence corresponding to a certain foreground region (equals one). */\r
+    static const float CERTAIN_FOREGROUND_CONFIDENCE=1.0f;\r
+\r
+    /** Confidence for a region likely being foreground.*/\r
+    static const float FOREGROUND_CONFIDENCE=0.8f;\r
+\r
+    /** Confidence for foreground or background type being equally likely.*/\r
+    static const float UNKNOWN_REGION_CONFIDENCE=0.5f;\r
+\r
+    /** Confidence for a region likely being background.*/\r
+    static const float BACKGROUND_CONFIDENCE=0.1f;\r
+\r
+    /** Confidence corresponding to a certain background reagion (equals zero). */\r
+    static const float CERTAIN_BACKGROUND_CONFIDENCE=0.0f;\r
+\r
+\r
+    /**\r
+     * Constructs a SioxSegmentator Object to be used for image segmentation.\r
+     *\r
+     * @param w X resolution of the image to be segmentated.\r
+     * @param h Y resolution of the image to be segmentated.\r
+     * @param limits Size of the cluster on LAB axises.\r
+     *        If <code>null</code>, the default value {0.64f,1.28f,2.56f}\r
+     *        is used.\r
+     */\r
+    SioxSegmentator(int w, int h, float *limitsArg, int limitsSize);\r
+\r
+    /**\r
+     * Destructor\r
+     */\r
+    virtual ~SioxSegmentator();\r
+\r
+\r
+    /**\r
+     * Segmentates the given image with information from the confidence\r
+     * matrix.\r
+     * <P>\r
+     * The confidence entries  of <code>BACKGROUND_CONFIDENCE</code> or less\r
+     * are mark known background pixel for the segmentation, those\r
+     * of at least <code>FOREGROUND_CONFIDENCE</code> mark known\r
+     * foreground pixel for the segmentation. Any other entry is treated\r
+     * as region of unknown affiliation.\r
+     * <P>\r
+     * As result, each pixel is classified either as foregroound or\r
+     * background, stored back into its <code>cm</code> entry as confidence\r
+     * <code>CERTAIN_FOREGROUND_CONFIDENCE</code> or\r
+     * <code>CERTAIN_BACKGROUND_CONFIDENCE</code>.\r
+     *\r
+     * @param image Pixel data of the image to be segmentated.\r
+     *        Every integer represents one ARGB-value.\r
+     * @param imageSize number of values in image\r
+     * @param cm Confidence matrix specifying the probability of an image\r
+     *        belonging to the foreground before and after the segmentation.\r
+     * @param smoothness Number of smoothing steps in the post processing.\r
+     * @param sizeFactorToKeep Segmentation retains the largest connected\r
+     *        foreground component plus any component with size at least\r
+     *        <CODE>sizeOfLargestComponent/sizeFactorToKeep</CODE>.\r
+     * @return <CODE>true</CODE> if the segmentation algorithm succeeded,\r
+     *         <CODE>false</CODE> if segmentation is impossible\r
+     */\r
+    bool segmentate(long *image, int imageSize,\r
+                    float *cm, int cmSize,\r
+                    int smoothness, double sizeFactorToKeep);\r
+\r
+    /**\r
+     * Clears given confidence matrix except entries for the largest connected\r
+     * component and every component with\r
+     * <CODE>size*sizeFactorToKeep >= sizeOfLargestComponent</CODE>.\r
+     *\r
+     * @param cm  Confidence matrix to be analysed\r
+     * @param threshold Pixel visibility threshold.\r
+     *        Exactly those cm entries larger than threshold are considered\r
+     *        to be a "visible" foreground pixel.\r
+     * @param sizeFactorToKeep This method keeps the largest connected\r
+     *        component plus any component with size at least\r
+     *        <CODE>sizeOfLargestComponent/sizeFactorToKeep</CODE>.\r
+     */\r
+    void keepOnlyLargeComponents(float *cm, int cmSize,\r
+                                 float threshold,\r
+                                 double sizeFactorToKeep);\r
+\r
+    /**\r
+     * Depth first search pixels in a foreground component.\r
+     *\r
+     * @param cm confidence matrix to be searched.\r
+     * @param i starting position as index to confidence matrix.\r
+     * @param threshold defines the minimum value at which a pixel is\r
+     *        considered foreground.\r
+     * @param curlabel label no of component.\r
+     * @return size in pixel of the component found.\r
+     */\r
+    int depthFirstSearch(float *cm, int i, float threshold, int curLabel);\r
+\r
+    /**\r
+     * Refines the classification stored in the confidence matrix by modifying\r
+     * the confidences for regions which have characteristics to both\r
+     * foreground and background if they fall into the specified square.\r
+     * <P>\r
+     * The can be used in displaying the image by assigning the alpha values\r
+     * of the pixels according to the confidence entries.\r
+     * <P>\r
+     * In the algorithm descriptions and examples GUIs this step is referrered\r
+     * to as <EM>Detail Refinement (Brush)</EM>.\r
+     *\r
+     * @param x Horizontal coordinate of the squares center.\r
+     * @param y Vertical coordinate of the squares center.\r
+     * @param brushmode Mode of the refinement applied, <CODE>ADD_EDGE</CODE>\r
+     *        or <CODE>SUB_EDGE</CODE>. Add mode only modifies pixels\r
+     *        formerly classified as background, sub mode only those\r
+     *        formerly classified as foreground.\r
+     * @param threshold Threshold for the add and sub refinement, deciding\r
+     *        at the confidence level to stop at.\r
+     * @param cf The confidence matrix to modify, generated by\r
+     *        <CODE>segmentate</CODE>, possibly already refined by privious\r
+     *        calls to <CODE>subpixelRefine</CODE>.\r
+     * @param brushsize Halfed diameter of the square shaped brush.\r
+     *\r
+     * @see #segmentate\r
+     */\r
+    void SioxSegmentator::subpixelRefine(int x, int y, int brushmode,\r
+                             float threshold, float *cf, int brushsize);\r
+\r
+    /**\r
+     * Refines the classification stored in the confidence matrix by modifying\r
+     * the confidences for regions which have characteristics to both\r
+     * foreground and background if they fall into the specified area.\r
+     * <P>\r
+     * The can be used in displaying the image by assigning the alpha values\r
+     * of the pixels according to the confidence entries.\r
+     * <P>\r
+     * In the algorithm descriptions and examples GUIs this step is referrered\r
+     * to as <EM>Detail Refinement (Brush)</EM>.\r
+     *\r
+     * @param area Area in which the reworking of the segmentation is\r
+     *        applied to.\r
+     * @param brushmode Mode of the refinement applied, <CODE>ADD_EDGE</CODE>\r
+     *        or <CODE>SUB_EDGE</CODE>. Add mode only modifies pixels\r
+     *        formerly classified as background, sub mode only those\r
+     *        formerly classified as foreground.\r
+     * @param threshold Threshold for the add and sub refinement, deciding\r
+     *        at the confidence level to stop at.\r
+     * @param cf The confidence matrix to modify, generated by\r
+     *        <CODE>segmentate</CODE>, possibly already refined by privious\r
+     *        calls to <CODE>subpixelRefine</CODE>.\r
+     *\r
+     * @see #segmentate\r
+     */\r
+    bool SioxSegmentator::subpixelRefine(int xa, int ya, int dx, int dy,\r
+                                     int brushmode,\r
+                                     float threshold, float *cf);\r
+    /**\r
+     * A region growing algorithms used to fill up the confidence matrix\r
+     * with <CODE>CERTAIN_FOREGROUND_CONFIDENCE</CODE> for corresponding\r
+     * areas of equal colors.\r
+     * <P>\r
+     * Basically, the method works like the <EM>Magic Wand<EM> with a\r
+     * tolerance threshold of zero.\r
+     *\r
+     * @param cm confidence matrix to be searched\r
+     * @param image image to be searched\r
+     */\r
+    void fillColorRegions(float *cm, int cmSize, long *image);\r
+\r
+private:\r
+\r
+    /**\r
+     * Prevent this from being used\r
+     */\r
+    SioxSegmentator();\r
+\r
+    /** error logging **/\r
+    void error(char *format, ...);\r
+\r
+    /** trace logging **/\r
+    void trace(char *format, ...);\r
+\r
+    typedef enum\r
+        {\r
+        ADD_EDGE, /** Add mode for the subpixel refinement. */\r
+        SUB_EDGE  /** Subtract mode for the subpixel refinement. */\r
+        } BrushMode;\r
+\r
+    // instance fields:\r
+\r
+    /** Horizontal resolution of the image to be segmentated. */\r
+    int imgWidth;\r
+\r
+    /** Vertical resolution of the image to be segmentated. */\r
+    int imgHeight;\r
+\r
+    /** Stores component label (index) by pixel it belongs to. */\r
+    int *labelField;\r
+\r
+    /**\r
+     * LAB color values of pixels that are definitly known background.\r
+     * Entries are of form {l,a,b}.\r
+     */\r
+    std::vector<CLAB> knownBg;\r
+\r
+    /**\r
+     * LAB color values of pixels that are definitly known foreground.\r
+     * Entries are of form {l,a,b}.\r
+     */\r
+    std::vector<CLAB> knownFg;\r
+\r
+    /** Holds background signature (a characteristic subset of the bg.) */\r
+    std::vector<CLAB> bgSignature;\r
+\r
+    /** Holds foreground signature (a characteristic subset of the fg).*/\r
+    std::vector<CLAB> fgSignature;\r
+\r
+    /** Size of cluster on lab axis. */\r
+    float *limits;\r
+\r
+    /** Maximum distance of two lab values. */\r
+    float clusterSize;\r
+\r
+    /**\r
+     * Stores Tupels for fast access to nearest background/foreground pixels.\r
+     */\r
+    std::map<long, Tupel> hs;\r
+\r
+    /** Size of the biggest blob.*/\r
+    int regionCount;\r
+\r
+    /** Copy of the original image, needed for detail refinement. */\r
+    long *origImage;\r
+    long origImageSize;\r
+\r
+    /** A flag that stores if the segmentation algorithm has already ran.*/\r
+    bool segmentated;\r
+\r
+};\r
+\r
+} //namespace siox\r
+} //namespace org\r
+\r
+#endif /* __SIOX_SEGMENTATOR_H__ */\r
+\r