summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: db705a6)
raw | patch | inline | side by side (parent: db705a6)
author | ishmal <ishmal@users.sourceforge.net> | |
Fri, 24 Mar 2006 14:40:42 +0000 (14:40 +0000) | ||
committer | ishmal <ishmal@users.sourceforge.net> | |
Fri, 24 Mar 2006 14:40:42 +0000 (14:40 +0000) |
src/trace/Makefile_insert | patch | blob | history | |
src/trace/siox-segmentator.cpp | [new file with mode: 0644] | patch | blob |
src/trace/siox-segmentator.h | [new file with mode: 0644] | patch | blob |
index 92686be0bdda604d8379901e96d8ebba027c5377..6a2100207ca4622eaa05c6d4f1f3a8486bc50ef6 100644 (file)
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
--- /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
--- /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