From e7ff0441bda2f141460de781018670e4c30f8534 Mon Sep 17 00:00:00 2001 From: joncruz Date: Sun, 4 Jun 2006 08:10:07 +0000 Subject: [PATCH] Applied patch 1500171 --- src/trace/siox.cpp | 2277 ++++++++++++++++++--------------- src/trace/siox.h | 746 ++++++----- src/trace/trace.cpp | 169 +-- src/ui/dialog/tracedialog.cpp | 7 +- 4 files changed, 1769 insertions(+), 1430 deletions(-) diff --git a/src/trace/siox.cpp b/src/trace/siox.cpp index 7975d9400..d460d1557 100644 --- a/src/trace/siox.cpp +++ b/src/trace/siox.cpp @@ -17,1124 +17,1138 @@ */ #include "siox.h" -#include - -#include //for error() and trace() -#include //sqrt(), pow(), round(), etc +#include +#include +#include namespace org { -namespace siox -{ - -//######################################################################## -//## U T I L S (originally Utils.java) -//######################################################################## - -/** - * Collection of auxiliary image processing methods used by the - * SioxSegmentator mainly for postprocessing. - * - * @author G. Friedland, K. Jantz, L. Knipping - * @version 1.05 - * - * Conversion to C++ by Bob Jamison - * - */ - -/** Caches color conversion values to speed up RGB->CIELAB conversion.*/ -static std::map RGB_TO_LAB; - -//forward decls -static void premultiplyMatrix(float alpha, float *cm, int cmSize); -//static float colordiffsq(unsigned long rgb0, unsigned long rgb1); -//static int getAlpha(unsigned long argb); -static int getRed(unsigned long rgb); -static int getGreen(unsigned long rgb); -static int getBlue(unsigned long rgb); -//static unsigned long packPixel(int a, int r, int g, int b); -static CLAB rgbToClab(unsigned 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) +namespace siox { - for (int y=0; ycm[idx]) - cm[idx]=cm[idx+1]; - } - } - for (int y=0; y=1; x--) { - int idx=(y*xres)+x; - if (cm[idx-1]>cm[idx]) - cm[idx]=cm[idx-1]; - } - } - for (int y=0; y cm[idx]) - cm[idx]=cm[((y+1)*xres)+x]; - } - } - for (int y=yres-1; y>=1; y--) { - for (int x=0; x cm[idx]) - cm[idx]=cm[((y-1)*xres)+x]; - } - } -} -/** - * Applies the morphological erode operator. - * - * @param cm Confidence matrix to be processed. - * @param xres Horizontal resolution of the matrix. - * @param yres Vertical resolution of the matrix. - */ -static void erode(float *cm, int xres, int yres) -{ - for (int y=0; y=1; x--) { - int idx=(y*xres)+x; - if (cm[idx-1] < cm[idx]) - cm[idx]=cm[idx-1]; - } - } - for (int y=0; y=1; y--) { - for (int x=0; x clabLookupTable; /** - * Blurs confidence matrix with a given symmetrically weighted kernel. - *

- * 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. + * Convert integer A, R, G, B values into an pixel value. */ -static void smoothcm(float *cm, int xres, int yres, - float f1, float f2, float f3) +static unsigned long getRGB(int a, int r, int g, int b) { - for (int y=0; y=2; x--) { - int idx=(y*xres)+x; - cm[idx]=f3*cm[idx-2]+f2*cm[idx-1]+f1*cm[idx]; - } - } - for (int y=0; y=2; y--) { - for (int x=0; x255) a=255; -/** - * Squared Euclidian distance of p and q. - *

- * 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; i255) r=255; -/** - * Squared Euclidian distance of p and q. - *

- * 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; -} + if (g<0) g=0; + else if (g>255) g=255; -/** - * 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)); -} -*/ + if (b<0) b=0; + else if (b>255) b=255; -/** - * 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)); + return (a<<24)|(r<<16)|(g<<8)|b; } -*/ - -/** - * Computes squared euclidian distance of two RGB color values. - *

- * Note: Faster to compute than colordiff - * - * @param rgb0 First color value. - * @param rgb1 Second color value. - * @return Squared Euclidian distance between the two color values. - */ -/* -static float colordiffsq(long rgb0, long rgb1) -{ - int rDist=getRed(rgb0) - getRed(rgb1); - int gDist=getGreen(rgb0) - getGreen(rgb1); - int bDist=getBlue(rgb0) - getBlue(rgb1); - return (float)(rDist*rDist+gDist*gDist+bDist*bDist); -} -*/ -/** - * Averages two ARGB colors. - * - * @param argb0 First color value. - * @param argb1 Second color value. - * @return The averaged ARGB color. - */ -/* -static long average(long argb0, long argb1) -{ - long ret = packPixel( - (getAlpha(argb0) + getAlpha(argb1))/2, - (getRed(argb0) + getRed(argb1) )/2, - (getGreen(argb0) + getGreen(argb1))/2, - (getBlue(argb0) + getBlue(argb1) )/2); - return ret; -} -*/ /** - * Computes squared euclidian distance in CLAB space for two colors - * given as RGB values. - * - * @param rgb0 First color value. - * @param rgb1 Second color value. - * @return Squared Euclidian distance in CLAB space. + * Convert float A, R, G, B values (0.0-1.0) into an pixel value. */ -static float labcolordiffsq(unsigned long rgb1, unsigned long rgb2) +static unsigned long getRGB(float a, float r, float g, float b) { - 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; + return getRGB((int)(a * 256.0), + (int)(r * 256.0), + (int)(g * 256.0), + (int)(b * 256.0)); } -/** - * 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(unsigned long rgb0, unsigned long rgb1) -{ - return (float)sqrt(labcolordiffsq(rgb0, rgb1)); -} - /** - * Converts 24-bit RGB values to {l,a,b} float values. - *

- * The conversion used is decribed at - * CLAB Conversion - * for reference white D65. Note that that the conversion is computational - * expensive. Result are cached to speed up further conversion calls. - * - * @param rgb RGB color value, - * @return CLAB color value tripel. + * Construct this CLAB from a packed-pixel ARGB value */ -static CLAB rgbToClab(unsigned long rgb) +CLAB::CLAB(unsigned long rgb) { - std::map::iterator iter = RGB_TO_LAB.find(rgb); - if (iter != RGB_TO_LAB.end()) + //First try looking up in the cache + std::map::iterator iter; + iter = clabLookupTable.find(rgb); + if (iter != clabLookupTable.end()) { CLAB res = iter->second; - return res; + C = res.C; + L = res.L; + A = res.A; + B = res.B; } - 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 + int ir = (rgb>>16) & 0xff; + int ig = (rgb>> 8) & 0xff; + int ib = (rgb ) & 0xff; + + float fr = ((float)ir) / 255.0; + float fg = ((float)ig) / 255.0; + float fb = ((float)ib) / 255.0; - if (var_R>0.04045) - var_R=(float) pow((var_R+0.055f)/1.055f, 2.4); + if (fr > 0.04045) + fr = (float) pow((fr + 0.055) / 1.055, 2.4); else - var_R=var_R/12.92f; + fr = fr / 12.92; - if (var_G>0.04045) - var_G=(float) pow((var_G+0.055f)/1.055f, 2.4); + if (fg > 0.04045) + fg = (float) pow((fg + 0.055) / 1.055, 2.4); else - var_G=var_G/12.92f; + fg = fg / 12.92; - if (var_B>0.04045) - var_B=(float) pow((var_B+0.055f)/1.055f, 2.4); + if (fb > 0.04045) + fb = (float) pow((fb + 0.055) / 1.055, 2.4); else - var_B=var_B/12.92f; + fb = fb / 12.92; - var_R=var_R*100.0f; - var_G=var_G*100.0f; - var_B=var_B*100.0f; + fr = fr * 100.0; + fg = fg * 100.0; + fb = fb * 100.0; - // 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; + // Use white = D65 + float x = fr * 0.4124 + fg * 0.3576 + fb * 0.1805; + float y = fr * 0.2126 + fg * 0.7152 + fb * 0.0722; + float z = fr * 0.0193 + fg * 0.1192 + fb * 0.9505; - float var_X=X/95.047f; - float var_Y=Y/100.0f; - float var_Z=Z/108.883f; + float vx = x / 95.047; + float vy = y / 100.000; + float vz = z / 108.883; - if (var_X>0.008856f) - var_X=(float) pow(var_X, 0.3333f); + if (vx > 0.008856) + vx = (float) pow(vx, 0.3333); else - var_X=(7.787f*var_X)+(16.0f/116.0f); + vx = (7.787 * vx) + (16.0 / 116.0); - if (var_Y>0.008856f) - var_Y=(float) pow(var_Y, 0.3333f); + if (vy > 0.008856) + vy = (float) pow(vy, 0.3333); else - var_Y=(7.787f*var_Y)+(16.0f/116.0f); + vy = (7.787 * vy) + (16.0 / 116.0); - if (var_Z>0.008856f) - var_Z=(float) pow(var_Z, 0.3333f); + if (vz > 0.008856) + vz = (float) pow(vz, 0.3333); else - var_Z=(7.787f*var_Z)+(16.0f/116.0f); + vz = (7.787 * vz) + (16.0 / 116.0); - CLAB lab((116.0f*var_Y)-16.0f , 500.0f*(var_X-var_Y), 200.0f*(var_Y-var_Z)); + C = 0; + L = 116.0 * vy - 16.0; + A = 500.0 * (vx - vy); + B = 200.0 * (vy - vz); - RGB_TO_LAB[rgb] = lab; + // Cache for next time + clabLookupTable[rgb] = *this; - return lab; } + + /** - * Converts an CLAB value to a RGB color value. - *

- * This is the reverse operation to rgbToClab. - * @param clab CLAB value. - * @return RGB value. - * @see #rgbToClab + * Return this CLAB's value a a packed-pixel ARGB value */ -/* -static long clabToRGB(const CLAB &clab) +unsigned long CLAB::toRGB() { - 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 vy = (L + 16.0) / 116.0; + float vx = A / 500.0 + vy; + float vz = vy - B / 200.0; - 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); + float vx3 = vx * vx * vx; + float vy3 = vy * vy * vy; + float vz3 = vz * vz * vz; - if (var_yPow3>0.008856f) - var_Y=var_yPow3; + if (vy3 > 0.008856) + vy = vy3; else - var_Y=(var_Y-16.0f/116.0f)/7.787f; + vy = (vy - 16.0 / 116.0) / 7.787; - if (var_xPow3>0.008856f) - var_X=var_xPow3; + if (vx3 > 0.008856) + vx = vx3; else - var_X=(var_X-16.0f/116.0f)/7.787f; + vx = (vx - 16.0 / 116.0) / 7.787; - if (var_zPow3>0.008856f) - var_Z=var_zPow3; + if (vz3 > 0.008856) + vz = vz3; else - var_Z=(var_Z-16.0f/116.0f)/7.787f; + vz = (vz - 16.0 / 116.0) / 7.787; - 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 + float x = 95.047 * vx; //use white = D65 + float y = 100.000 * vy; + float z = 108.883 * vz; - 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 + vx = x / 100.0; + vy = y / 100.0; + vz = z / 100.0; - 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); + float vr =(float)(vx * 3.2406 + vy * -1.5372 + vz * -0.4986); + float vg =(float)(vx * -0.9689 + vy * 1.8758 + vz * 0.0415); + float vb =(float)(vx * 0.0557 + vy * -0.2040 + vz * 1.0570); - if (var_R>0.0031308f) - var_R=(float)(1.055f*pow(var_R, (1.0f/2.4f))-0.055f); + if (vr > 0.0031308) + vr = (float)(1.055 * pow(vr, (1.0 / 2.4)) - 0.055); else - var_R=12.92f*var_R; + vr = 12.92 * vr; - if (var_G>0.0031308f) - var_G=(float)(1.055f*pow(var_G, (1.0f/2.4f))-0.055f); + if (vg > 0.0031308) + vg = (float)(1.055 * pow(vg, (1.0 / 2.4)) - 0.055); else - var_G=12.92f*var_G; + vg = 12.92 * vg; - if (var_B>0.0031308f) - var_B=(float)(1.055f*pow(var_B, (1.0f/2.4f))-0.055f); + if (vb > 0.0031308) + vb = (float)(1.055 * pow(vb, (1.0 / 2.4)) - 0.055); else - var_B=12.92f*var_B; + vb = 12.92 * vb; - 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); + return getRGB(0.0, vr, vg, vb); } -*/ + /** - * 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. + * Computes squared euclidian distance in CLAB space for two colors + * given as RGB values. */ -static long setAlpha(int alpha, unsigned long rgb) +float CLAB::diffSq(unsigned int rgb1, unsigned int rgb2) { - if (alpha>255) - alpha=0; - else if (alpha<0) - alpha=0; - return (alpha<<24)|(rgb&0xFFFFFF); + CLAB c1(rgb1); + CLAB c2(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; } + /** - * 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. + * Computes squared euclidian distance in CLAB space for two colors + * given as RGB values. */ -static long setAlpha(float alpha, unsigned long rgb) +float CLAB::diff(unsigned int rgb0, unsigned int rgb1) { - return setAlpha((int)(255.0f*alpha), rgb); + return (float) sqrt(diffSq(rgb0, rgb1)); } + +//######################################################################## +//# T U P E L +//######################################################################## + /** - * 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. + * Helper class for storing the minimum distances to a cluster centroid + * in background and foreground and the index to the centroids in each + * signature for a given color. */ -/* -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; +class Tupel { +public: - return (a<<24)|(r<<16)|(g<<8)|b; -} -*/ + Tupel() + { + minBgDist = 0.0f; + indexMinBg = 0; + minFgDist = 0.0f; + indexMinFg = 0; + } + Tupel(float minBgDistArg, long indexMinBgArg, + float minFgDistArg, long indexMinFgArg) + { + minBgDist = minBgDistArg; + indexMinBg = indexMinBgArg; + minFgDist = minFgDistArg; + indexMinFg = indexMinFgArg; + } + Tupel(const Tupel &other) + { + minBgDist = other.minBgDist; + indexMinBg = other.indexMinBg; + minFgDist = other.minFgDist; + indexMinFg = other.indexMinFg; + } + Tupel &operator=(const Tupel &other) + { + minBgDist = other.minBgDist; + indexMinBg = other.indexMinBg; + minFgDist = other.minFgDist; + indexMinFg = other.indexMinFg; + return *this; + } + virtual ~Tupel() + {} + + float minBgDist; + long indexMinBg; + float minFgDist; + long indexMinFg; + + }; + + + +//######################################################################## +//# S I O X I M A G E +//######################################################################## /** - * Returns the alpha component of an ARGB value. - * - * @param argb An ARGB color value. - * @return The alpha component, ranging from 0 to 255. + * Create an image with the given width and height */ -/* -static int getAlpha(unsigned long argb) +SioxImage::SioxImage(unsigned int widthArg, unsigned int heightArg) { - return (argb>>24)&0xFF; + init(width, height); } -*/ /** - * 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. + * Copy constructor */ -static int getRed(unsigned long rgb) +SioxImage::SioxImage(const SioxImage &other) { - return (rgb>>16)&0xFF; + pixdata = NULL; + cmdata = NULL; + assign(other); } - /** - * 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. + * Assignment */ -static int getGreen(unsigned long rgb) +SioxImage &SioxImage::operator=(const SioxImage &other) { - return (rgb>>8)&0xFF; + assign(other); + return *this; } + /** - * 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. + * Clean up after use. */ -static int getBlue(unsigned long rgb) +SioxImage::~SioxImage() { - return (rgb)&0xFF; + if (pixdata) delete[] pixdata; + if (cmdata) delete[] cmdata; } /** - * 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. + * Returns true if the previous operation on this image + * was successful, else false. */ -/* -static std::string clabToString(const CLAB &clab) +bool SioxImage::isValid() { - 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; + return valid; } -*/ - -//######################################################################## -//## C O L O R S I G N A T U R E (originally ColorSignature.java) -//######################################################################## /** - * Representation of a color signature. - *

- * This class implements a clustering algorithm based on a modified kd-tree. - * The splitting rule is to simply divide the given interval into two equally - * sized subintervals. - * In the stageone(), approximate clusters are found by building - * up such a tree and stopping when an interval at a node has become smaller - * than the allowed cluster diameter, which is given by limits. - * At this point, clusters may be split in several nodes.
- * Therefore, in stagetwo(), nodes that belong to several clusters - * are recombined by another k-d tree clustering on the prior cluster - * centroids. To guarantee a proper level of abstraction, clusters that contain - * less than 0.01% of the pixels of the entire sample are removed. Please - * refer to the file NOTICE to get links to further documentation. - * - * @author Gerald Friedland, Lars Knipping - * @version 1.02 - * - * Conversion to C++ by Bob Jamison - * + * Sets whether an operation was successful, and whether + * this image should be considered a valid one. + * was successful, else false. */ +void SioxImage::setValid(bool val) +{ + valid = val; +} + /** - * 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 + * Set a pixel at the x,y coordinates to the given value. + * If the coordinates are out of range, do nothing. */ -static void stageone(std::vector &points, - int depth, - std::vector< std::vector > &clusters, - float *limits) +void SioxImage::setPixel(unsigned int x, + unsigned int y, + unsigned int pixval) { - if (points.size() < 1) + if (x > width || y > height) return; - - int dims=3; - int curdim=depth%dims; - float min = 0.0f; - float max = 0.0f; - if (curdim == 0) - { - min=points[0].C; - max=points[0].C; - // find maximum and minimum - for (unsigned int i=1; ipoints[i].C) - min=points[i].C; - if (maxpoints[i].L) - min=points[i].L; - if (maxpoints[i].A) - min=points[i].A; - if (maxpoints[i].B) - min=points[i].B; - if (maxlimits[curdim]) { // Split according to Rubner-Rule - // split - float pivotvalue=((max-min)/2.0f)+min; - - std::vector smallerpoints; // allocate mem - std::vector biggerpoints; - - for (unsigned int i=0; i &points, - int depth, - std::vector< std::vector > &clusters, - float *limits, int total, float threshold) +void SioxImage::setPixel(unsigned int x, unsigned int y, + unsigned int a, + unsigned int r, + unsigned int g, + unsigned int b) { - if (points.size() < 1) + if (x > width || y > height) return; + unsigned long offset = width * y + x; + unsigned int pixval = ((a << 24) & 0xff000000) | + ((r << 16) & 0x00ff0000) | + ((g << 8) & 0x0000ff00) | + ((b ) & 0x000000ff); + pixdata[offset] = pixval; +} - int curdim=depth%3; // without cardinality - float min = 0.0f; - float max = 0.0f; - if (curdim == 0) - { - min=points[0].L; - max=points[0].L; - // find maximum and minimum - for (unsigned int i=1; ipoints[i].L) - min=points[i].L; - if (maxpoints[i].A) - min=points[i].A; - if (maxpoints[i].B) - min=points[i].B; - if (maxlimits[curdim]) { // Split according to Rubner-Rule - // split - float pivotvalue=((max-min)/2.0f)+min; - - std::vector smallerpoints; // allocate mem - std::vector biggerpoints; - for (unsigned int i=0; i=threshold) { - CLAB point; - for (unsigned int i=0; i newCluster; - newCluster.push_back(point); - clusters.push_back(newCluster); - } - } +/** + * Get a pixel at the x,y coordinates given. If + * the coordinates are out of range, return 0; + */ +unsigned int SioxImage::getPixel(unsigned int x, unsigned int y) +{ + if (x > width || y > height) + return 0L; + unsigned long offset = width * y + x; + return pixdata[offset]; } /** - * 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 + * Return the image data buffer */ -static std::vector createSignature(std::vector &input, - float *limits, float threshold) +unsigned int *SioxImage::getImageData() { - std::vector< std::vector > clusters1; - std::vector< std::vector > clusters2; - - stageone(input, 0, clusters1, limits); - - std::vector centroids; - for (unsigned int i=0; i cluster = clusters1[i]; - CLAB centroid; // +1 for the cardinality - for (unsigned int k=0; k see paper by tomasi - - std::vector res; - for (unsigned int i=0 ; i width || y > height) + return; + unsigned long offset = width * y + x; + cmdata[offset] = confval; +} +/** + * Get a confidence valueat the x,y coordinates given. If + * the coordinates are out of range, return 0; + */ +float SioxImage::getConfidence(unsigned int x, unsigned int y) +{ + if (x > width || y > height) + return 0.0; + unsigned long offset = width * y + x; + return cmdata[offset]; +} -//######################################################################## -//## S I O X S E G M E N T A T O R (originally SioxSegmentator.java) -//######################################################################## - -//### NOTE: Doxygen comments are in siox.h +/** + * Return the confidence data buffer + */ +float *SioxImage::getConfidenceData() +{ + return cmdata; +} -/** Confidence corresponding to a certain foreground region (equals one). */ -const float SioxSegmentator::CERTAIN_FOREGROUND_CONFIDENCE=1.0f; -/** Confidence for a region likely being foreground.*/ -const float SioxSegmentator::FOREGROUND_CONFIDENCE=0.8f; +/** + * Return the width of this image + */ +int SioxImage::getWidth() +{ + return width; +} -/** Confidence for foreground or background type being equally likely.*/ -const float SioxSegmentator::UNKNOWN_REGION_CONFIDENCE=0.5f; +/** + * Return the height of this image + */ +int SioxImage::getHeight() +{ + return height; +} -/** Confidence for a region likely being background.*/ -const float SioxSegmentator::BACKGROUND_CONFIDENCE=0.1f; +/** + * Initialize values. Used by constructors + */ +void SioxImage::init(unsigned int widthArg, unsigned int heightArg) +{ + valid = true; + width = widthArg; + height = heightArg; + imageSize = width * height; + pixdata = new unsigned int[imageSize]; + cmdata = new float[imageSize]; + for (unsigned long i=0 ; i>24) & 0xff; + unsigned int r = ((rgb>>16) & 0xff); + unsigned int g = ((rgb>> 8) & 0xff); + unsigned int b = ((rgb ) & 0xff); + fputc((unsigned char) r, f); + fputc((unsigned char) g, f); + fputc((unsigned char) b, f); + } + } + fclose(f); + return true; +} + + +#ifdef HAVE_GLIB + +/** + * Create an image from a GdkPixbuf + */ +SioxImage::SioxImage(GdkPixbuf *buf) +{ + if (!buf) + return; + + unsigned int width = gdk_pixbuf_get_width(buf); + unsigned int height = gdk_pixbuf_get_height(buf); + init(width, height); //DO THIS NOW!! + + + guchar *pixldata = gdk_pixbuf_get_pixels(buf); + int rowstride = gdk_pixbuf_get_rowstride(buf); + int n_channels = gdk_pixbuf_get_n_channels(buf); + + //### Fill in the cells with RGB values + int row = 0; + for (unsigned int y=0 ; y> 16) & 0xff; + p[1] = (rgb >> 8) & 0xff; + p[2] = (rgb ) & 0xff; + p += n_channels; + } + row += rowstride; + } + + return buf; +} + +#endif /* GLIB */ - labelField = new int[pixelCount]; - 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 knownBg; + std::vector knownFg; + for (int x = 0 ; x < workImage.getWidth() ; x++) + for (int y = 0 ; y < workImage.getHeight() ; y++) + { + float cm = workImage.getConfidence(x, y); + unsigned int pix = workImage.getPixel(x, y); + if (cm <= BACKGROUND_CONFIDENCE) + knownBg.push_back(pix); //note: uses CLAB(rgb) + else if (cm >= FOREGROUND_CONFIDENCE) + knownFg.push_back(pix); + } + + trace("knownBg:%d knownFg:%d", knownBg.size(), knownFg.size()); - // create color signatures - for (int i=0; i=FOREGROUND_CONFIDENCE) - knownFg.push_back(rgbToClab(image[i])); - } - bgSignature = createSignature(knownBg, limits, BACKGROUND_CONFIDENCE); - fgSignature = createSignature(knownFg, limits, BACKGROUND_CONFIDENCE); + std::vector bgSignature ; + if (!colorSignature(knownBg, bgSignature, 3)) + { + error("Could not create background signature"); + workImage.setValid(false); + return workImage; + } + std::vector fgSignature ; + if (!colorSignature(knownFg, fgSignature, 3)) + { + error("Could not create foreground signature"); + delete[] labelField; + workImage.setValid(false); + return workImage; + } + + //trace("### bgSignature:%d", bgSignature.size()); - if (bgSignature.size() < 1) { + if (bgSignature.size() < 1) + { // segmentation impossible - return false; - } + error("Signature size is < 1. Segmentation is impossible"); + delete[] labelField; + workImage.setValid(false); + return workImage; + } // classify using color signatures, // classification cached in hashmap for drb and speedup purposes - for (int i=0; i=FOREGROUND_CONFIDENCE) { - cm[i]=CERTAIN_FOREGROUND_CONFIDENCE; - continue; - } - if (cm[i]>BACKGROUND_CONFIDENCE) { - bool isBackground=true; - std::map::iterator iter = hs.find(i); - Tupel tupel(0.0f, 0, 0.0f, 0); - if (iter == hs.end()) { - CLAB lab = rgbToClab(image[i]); + std::map hs; + + for (unsigned int i=0; i= FOREGROUND_CONFIDENCE) + { + cm[i] = CERTAIN_FOREGROUND_CONFIDENCE; + } + else if (cm[i] <= BACKGROUND_CONFIDENCE) + { + cm[i] = CERTAIN_BACKGROUND_CONFIDENCE; + } + else // somewhere in between + { + bool isBackground = true; + std::map::iterator iter = hs.find(i); + if (iter != hs.end()) //found + { + Tupel tupel = iter->second; + isBackground = tupel.minBgDist <= tupel.minFgDist; + } + else + { + CLAB lab(image[i]); float minBg = sqrEuclidianDist(lab, bgSignature[0]); int minIndex=0; - for (unsigned int j=1; j=UNKNOWN_REGION_CONFIDENCE) { - cm[i]=CERTAIN_FOREGROUND_CONFIDENCE; - } else { - cm[i]=CERTAIN_BACKGROUND_CONFIDENCE; + for (unsigned int i=0; i < pixelCount; i++) + { + if (cm[i] >= UNKNOWN_REGION_CONFIDENCE) + cm[i] = CERTAIN_FOREGROUND_CONFIDENCE; + else + cm[i] = CERTAIN_BACKGROUND_CONFIDENCE; + } + + keepOnlyLargeComponents(UNKNOWN_REGION_CONFIDENCE, 1.5/*sizeFactorToKeep*/); + fillColorRegions(); + dilate(cm, width, height); + + delete[] labelField; + + //#### Yaay. We are done. Now clear everything but the background + for (unsigned int y = 0 ; y < height ; y++) + for (unsigned int x = 0 ; x < width ; x++) + { + float conf = workImage.getConfidence(x, y); + if (conf < FOREGROUND_CONFIDENCE) + { + workImage.setPixel(x, y, backgroundFillColor); + } + } + + trace("### Done"); + return workImage; +} + + + +//############## +//## PRIVATE +//############## + +/** + * Initialize the Siox engine to its 'pristine' state. + * Performed at the beginning of extractForeground(). + */ +void Siox::init() +{ + limits[0] = 0.64f; + limits[1] = 1.28f; + limits[2] = 2.56f; + + float negLimits[3]; + negLimits[0] = -limits[0]; + negLimits[1] = -limits[1]; + negLimits[2] = -limits[2]; + + clusterSize = sqrEuclidianDist(limits, 3, negLimits); +} + + +/** + * Clean up any debris from processing. + */ +void Siox::cleanup() +{ +} + + + + +/** + * Stage 1 of the color signature work. 'dims' will be either + * 2 for grays, or 3 for colors + */ +void Siox::colorSignatureStage1(CLAB *points, + unsigned int leftBase, + unsigned int rightBase, + unsigned int recursionDepth, + unsigned int *clusterCount, + const unsigned int dims) +{ + + unsigned int currentDim = recursionDepth % dims; + CLAB point = points[leftBase]; + float min = point(currentDim); + float max = min; + + for (unsigned int i = leftBase + 1; i < rightBase ; i++) + { + point = points[i]; + float curval = point(currentDim); + if (curval < min) min = curval; + if (curval > max) max = curval; + } + + //Do the Rubner-rule split (sounds like a dance) + if (max - min > limits[currentDim]) + { + float pivotPoint = (min + max) / 2.0; //average + unsigned int left = leftBase; + unsigned int right = rightBase - 1; + + //# partition points according to the dimension + while (true) + { + while ( true ) + { + point = points[left]; + if (point(currentDim) > pivotPoint) + break; + left++; + } + while ( true ) + { + point = points[right]; + if (point(currentDim) <= pivotPoint) + break; + right--; + } + + if (left > right) + break; + + point = points[left]; + points[left] = points[right]; + points[right] = point; + + left++; + right--; + } + + //# Recurse and create sub-trees + colorSignatureStage1(points, leftBase, left, + recursionDepth + 1, clusterCount, dims); + colorSignatureStage1(points, left, rightBase, + recursionDepth + 1, clusterCount, dims); + } + else + { + //create a leaf + CLAB newpoint; + + newpoint.C = rightBase - leftBase; + + for (; leftBase < rightBase ; leftBase++) + { + newpoint.add(points[leftBase]); + } + + //printf("clusters:%d\n", *clusters); + + if (newpoint.C != 0) + newpoint.mul(1.0 / (float)newpoint.C); + points[*clusterCount] = newpoint; + (*clusterCount)++; } - } +} + + + +/** + * Stage 2 of the color signature work + */ +void Siox::colorSignatureStage2(CLAB *points, + unsigned int leftBase, + unsigned int rightBase, + unsigned int recursionDepth, + unsigned int *clusterCount, + const float threshold, + const unsigned int dims) +{ + + + unsigned int currentDim = recursionDepth % dims; + CLAB point = points[leftBase]; + float min = point(currentDim); + float max = min; + + for (unsigned int i = leftBase+ 1; i < rightBase; i++) + { + point = points[i]; + float curval = point(currentDim); + if (curval < min) min = curval; + if (curval > max) max = curval; + } + + //Do the Rubner-rule split (sounds like a dance) + if (max - min > limits[currentDim]) + { + float pivotPoint = (min + max) / 2.0; //average + unsigned int left = leftBase; + unsigned int right = rightBase - 1; + + //# partition points according to the dimension + while (true) + { + while ( true ) + { + point = points[left]; + if (point(currentDim) > pivotPoint) + break; + left++; + } + while ( true ) + { + point = points[right]; + if (point(currentDim) <= pivotPoint) + break; + right--; + } + + if (left > right) + break; + + point = points[left]; + points[left] = points[right]; + points[right] = point; + + left++; + right--; + } + + //# Recurse and create sub-trees + colorSignatureStage2(points, leftBase, left, + recursionDepth + 1, clusterCount, threshold, dims); + colorSignatureStage2(points, left, rightBase, + recursionDepth + 1, clusterCount, threshold, dims); + } + else + { + //### Create a leaf + unsigned int sum = 0; + for (unsigned int i = leftBase; i < rightBase; i++) + sum += points[i].C; + + if ((float)sum >= threshold) + { + float scale = (float)(rightBase - leftBase); + CLAB newpoint; + + for (; leftBase < rightBase; leftBase++) + newpoint.add(points[leftBase]); + + if (scale != 0.0) + newpoint.mul(1.0 / scale); + points[*clusterCount] = newpoint; + (*clusterCount)++; + } + } +} + + + +/** + * Main color signature method + */ +bool Siox::colorSignature(const std::vector &inputVec, + std::vector &result, + const unsigned int dims) +{ + + unsigned int length = inputVec.size(); + + if (length < 1) // no error. just don't do anything + return true; + + CLAB *input = (CLAB *) malloc(length * sizeof(CLAB)); + + if (!input) + { + error("Could not allocate buffer for signature"); + return false; + } + for (unsigned int i=0 ; i < length ; i++) + input[i] = inputVec[i]; - keepOnlyLargeComponents(cm, UNKNOWN_REGION_CONFIDENCE, sizeFactorToKeep); - fillColorRegions(cm, image); - dilate(cm, imgWidth, imgHeight); + unsigned int stage1length = 0; + colorSignatureStage1(input, 0, length, 0, + &stage1length, dims); + + unsigned int stage2length = 0; + colorSignatureStage2(input, 0, stage1length, 0, + &stage2length, length * 0.001, dims); + + result.clear(); + for (unsigned int i=0 ; i < stage2length ; i++) + result.push_back(input[i]); + + free(input); - segmentated=true; return true; } -void SioxSegmentator::keepOnlyLargeComponents(float *cm, - float threshold, - double sizeFactorToKeep) +/** + * + */ +void Siox::keepOnlyLargeComponents(float threshold, + double sizeFactorToKeep) { - int idx = 0; - for (int i=0 ; i labelSizes; - for (int i=0 ; i=threshold) { - regionCount=depthFirstSearch(cm, i, threshold, curlabel++); + for (unsigned long i=0 ; i= threshold) + { + regionCount = depthFirstSearch(i, threshold, curlabel++); labelSizes.push_back(regionCount); - } + } - if (regionCount>maxregion) { - maxregion=regionCount; - maxblob=curlabel-1; + if (regionCount>maxregion) + { + maxregion = regionCount; + maxblob = curlabel-1; + } } - } - for (int i=0 ; i pixelsToVisit; - int componentSize=0; - if (labelField[i]==-1 && cm[i]>=threshold) { // label #i - labelField[i] = curLabel; - ++componentSize; - pixelsToVisit.push_back(i); - } - while (pixelsToVisit.size() > 0) { - int pos=pixelsToVisit[pixelsToVisit.size()-1]; - pixelsToVisit.erase(pixelsToVisit.end()-1); - int x=pos%imgWidth; - int y=pos/imgWidth; + int componentSize = 0; + + if (labelField[startPos]==-1 && cm[startPos]>=threshold) + { + labelField[startPos] = curLabel; + componentSize++; + pixelsToVisit.push_back(startPos); + } + + + while (pixelsToVisit.size() > 0) + { + int pos = pixelsToVisit[pixelsToVisit.size() - 1]; + pixelsToVisit.erase(pixelsToVisit.end() - 1); + unsigned int x = pos % width; + unsigned int y = pos / width; + // check all four neighbours - int left = pos-1; - if (x-1>=0 && labelField[left]==-1 && cm[left]>=threshold) { + int left = pos - 1; + if (x-1>=0 && labelField[left]==-1 && cm[left]>=threshold) + { labelField[left]=curLabel; - ++componentSize; + componentSize++; pixelsToVisit.push_back(left); - } - int right = pos+1; - if (x+1=threshold) { + } + + int right = pos + 1; + if (x+1 < width && labelField[right]==-1 && cm[right]>=threshold) + { labelField[right]=curLabel; - ++componentSize; + componentSize++; pixelsToVisit.push_back(right); - } - int top = pos-imgWidth; - if (y-1>=0 && labelField[top]==-1 && cm[top]>=threshold) { + } + + int top = pos - width; + if (y-1>=0 && labelField[top]==-1 && cm[top]>=threshold) + { labelField[top]=curLabel; - ++componentSize; + componentSize++; pixelsToVisit.push_back(top); - } - int bottom = pos+imgWidth; - if (y+1=threshold) { + } + + int bottom = pos + width; + if (y+1 < height && labelField[bottom]==-1 + && cm[bottom]>=threshold) + { labelField[bottom]=curLabel; - ++componentSize; + componentSize++; pixelsToVisit.push_back(bottom); - } - } - return componentSize; -} - -void SioxSegmentator::subpixelRefine(int x, int y, int brushmode, - float threshold, float *cf, int brushsize) -{ - subpixelRefine(x-brushsize, y-brushsize, - 2*brushsize, 2*brushsize, - brushmode, threshold, cf); -} - - -bool SioxSegmentator::subpixelRefine(int xa, int ya, int dx, int dy, - int brushmode, - float threshold, float *cf) -{ - if (!segmentated) { - error("no segmentation yet"); - return false; - } - - int x0 = (xa > 0) ? xa : 0; - int y0 = (ya > 0) ? ya : 0; - - int xTo = (imgWidth - 1 < xa+dx ) ? imgWidth-1 : xa+dx; - int yTo = (imgHeight - 1 < ya+dy ) ? imgHeight-1 : ya+dy; - - for (int ey=y0; ey::iterator iter = hs.find(val); - if (iter != hs.end()) { - minDistBg=(float) sqrt((float)iter->second.minBgDist); - minDistFg=(float) sqrt((float)iter->second.minFgDist); - } else { - continue; - } - if (ADD_EDGE == brushmode) { // handle adder - if (cf[ey*imgWidth+ex]FOREGROUND_CONFIDENCE) { - // foreground, we want to take something away - float alpha; - if (minDistBg==0) { - alpha=CERTAIN_BACKGROUND_CONFIDENCE; - } else { - alpha=CERTAIN_FOREGROUND_CONFIDENCE- - (minDistFg/minDistBg < CERTAIN_FOREGROUND_CONFIDENCE) ? // more background -> >1 - minDistFg/minDistBg : CERTAIN_FOREGROUND_CONFIDENCE; - // bg = gf -> 1 - // more fg -> <1 - } - if (alpha pixelsToVisit; - for (int i=0; i 0) { - int pos=pixelsToVisit[pixelsToVisit.size()-1]; - pixelsToVisit.erase(pixelsToVisit.end()-1); - int x=pos%imgWidth; - int y=pos/imgWidth; + while (pixelsToVisit.size() > 0) + { + int pos = pixelsToVisit[pixelsToVisit.size() - 1]; + pixelsToVisit.erase(pixelsToVisit.end() - 1); + unsigned int x=pos % width; + unsigned int y=pos / width; // check all four neighbours int left = pos-1; - if (x-1>=0 && labelField[left]==-1 - && labcolordiff(image[left], origColor)<1.0) { + if (x-1 >= 0 && labelField[left] == -1 + && CLAB::diff(image[left], origColor)<1.0) + { labelField[left]=curLabel; cm[left]=CERTAIN_FOREGROUND_CONFIDENCE; // ++componentSize; pixelsToVisit.push_back(left); - } + } int right = pos+1; - if (x+1=0 && labelField[top]==-1 - && labcolordiff(image[top], origColor)<1.0) { + && CLAB::diff(image[top], origColor)<1.0) + { labelField[top]=curLabel; cm[top]=CERTAIN_FOREGROUND_CONFIDENCE; // ++componentSize; pixelsToVisit.push_back(top); - } - int bottom = pos+imgWidth; - if (y+1maxRegion) { // maxRegion=componentSize; //} - } + } +} + + + + +/** + * Applies the morphological dilate operator. + * + * Can be used to close small holes in the given confidence matrix. + */ +void Siox::dilate(float *cm, int xres, int yres) +{ + + for (int y=0; ycm[idx]) + cm[idx]=cm[idx+1]; + } + } + + for (int y=0; y=1; x--) + { + int idx=(y*xres)+x; + if (cm[idx-1]>cm[idx]) + cm[idx]=cm[idx-1]; + } + } + + for (int y=0; y cm[idx]) + cm[idx]=cm[((y+1)*xres)+x]; + } + } + + for (int y=yres-1; y>=1; y--) + { + for (int x=0; x cm[idx]) + cm[idx]=cm[((y-1)*xres)+x]; + } + } +} + +/** + * Applies the morphological erode operator. + */ +void Siox::erode(float *cm, int xres, int yres) +{ + for (int y=0; y=1; x--) + { + int idx=(y*xres)+x; + if (cm[idx-1] < cm[idx]) + cm[idx]=cm[idx-1]; + } + } + for (int y=0; y=1; y--) + { + for (int x=0; x max) + max=cm[i]; + + if (max<=0.0 || max==1.0) + return; + + float alpha=1.00f/max; + premultiplyMatrix(alpha, cm, cmSize); +} + +/** + * Multiplies matrix with the given scalar. + */ +void Siox::premultiplyMatrix(float alpha, float *cm, int cmSize) +{ + for (int i=0; i=2; x--) + { + int idx=(y*xres)+x; + cm[idx]=f3*cm[idx-2]+f2*cm[idx-1]+f1*cm[idx]; + } + } + for (int y=0; y=2; y--) + { + for (int x=0; x +#include - http://www.apache.org/licenses/LICENSE-2.0 +#define HAVE_GLIB - 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. - */ +#ifdef HAVE_GLIB +#include +#include +#endif -#include -#include namespace org { + namespace siox { -/** - * Image segmentator based on - *SIOX: Simple Interactive Object Extraction. - *

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

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

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

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

- * Algorithm idea by Gerald Friedland. - * - * @author Gerald Friedland, Kristian Jantz, Lars Knipping - * @version 1.12 - */ + +//######################################################################## +//# C L A B +//######################################################################## /** - * Helper class for storing the minimum distances to a cluster centroid - * in background and foreground and the index to the centroids in each - * signature for a given color. + * */ -class Tupel { +class CLAB +{ public: - Tupel() - { - minBgDist = 0.0f; - indexMinBg = 0; - minFgDist = 0.0f; - indexMinFg = 0; - } - Tupel(float minBgDistArg, long indexMinBgArg, - float minFgDistArg, long indexMinFgArg) - { - minBgDist = minBgDistArg; - indexMinBg = indexMinBgArg; - minFgDist = minFgDistArg; - indexMinFg = indexMinFgArg; - } - Tupel(const Tupel &other) - { - minBgDist = other.minBgDist; - indexMinBg = other.indexMinBg; - minFgDist = other.minFgDist; - indexMinFg = other.indexMinFg; - } - Tupel &operator=(const Tupel &other) + /** + * + */ + CLAB() { - minBgDist = other.minBgDist; - indexMinBg = other.indexMinBg; - minFgDist = other.minFgDist; - indexMinFg = other.indexMinFg; - return *this; + C = 0; + L = A = B = 0.0f; } - virtual ~Tupel() - {} - float minBgDist; - long indexMinBg; - float minFgDist; - long indexMinFg; - }; + /** + * + */ + CLAB(unsigned long rgb); -class CLAB -{ -public: - CLAB() - { - C = L = A = B = 0.0f; - } + /** + * + */ CLAB(float lArg, float aArg, float bArg) { - C = 0.0f; + C = 0; L = lArg; A = aArg; B = bArg; } + + + /** + * + */ CLAB(const CLAB &other) { C = other.C; @@ -133,6 +96,11 @@ public: A = other.A; B = other.B; } + + + /** + * + */ CLAB &operator=(const CLAB &other) { C = other.C; @@ -141,260 +109,458 @@ public: B = other.B; return *this; } + + /** + * + */ virtual ~CLAB() {} - float C; + /** + * Retrieve a CLAB value via index. + */ + virtual float operator()(unsigned int index) + { + if (index==0) return L; + else if (index==1) return A; + else if (index==2) return B; + else return 0; + } + + + /** + * + */ + virtual void add(const CLAB &other) + { + C += other.C; + L += other.L; + A += other.A; + B += other.B; + } + + + /** + * + */ + virtual void mul(float scale) + { + L *= scale; + A *= scale; + B *= scale; + } + + + /** + * + */ + virtual unsigned long toRGB(); + + /** + * Computes squared euclidian distance in CLAB space for two colors + * given as RGB values. + */ + static float diffSq(unsigned int rgb1, unsigned int rgb2); + + /** + * Computes squared euclidian distance in CLAB space for two colors + * given as RGB values. + */ + static float diff(unsigned int rgb0, unsigned int rgb1); + + + unsigned int C; float L; float A; float B; + + }; -class SioxSegmentator +//######################################################################## +//# S I O X I M A G E +//######################################################################## + +/** + * This is a generic image type that provides a consistent interface + * to Siox, so that developers will not need to worry about data arrays. + */ +class SioxImage { public: - /** Confidence corresponding to a certain foreground region (equals one). */ - static const float CERTAIN_FOREGROUND_CONFIDENCE; //=1.0f; + /** + * Create an image with the given width and height + */ + SioxImage(unsigned int width, unsigned int height); - /** Confidence for a region likely being foreground.*/ - static const float FOREGROUND_CONFIDENCE; //=0.8f; + /** + * Copy constructor + */ + SioxImage(const SioxImage &other); - /** Confidence for foreground or background type being equally likely.*/ - static const float UNKNOWN_REGION_CONFIDENCE; //=0.5f; + /** + * Assignment + */ + SioxImage &operator=(const SioxImage &other); - /** Confidence for a region likely being background.*/ - static const float BACKGROUND_CONFIDENCE; //=0.1f; + /** + * Clean up after use. + */ + virtual ~SioxImage(); - /** Confidence corresponding to a certain background reagion (equals zero). */ - static const float CERTAIN_BACKGROUND_CONFIDENCE; //=0.0f; + /** + * Returns true if the previous operation on this image + * was successful, else false. + */ + virtual bool isValid(); + /** + * Sets whether an operation was successful, and whether + * this image should be considered a valid one. + * was successful, else false. + */ + virtual void setValid(bool val); /** - * Constructs a SioxSegmentator Object to be used for image segmentation. - * - * @param w X resolution of the image to be segmentated. - * @param h Y resolution of the image to be segmentated. - * @param limits Size of the cluster on LAB axises. - * If null, the default value {0.64f,1.28f,2.56f} - * is used. + * Set a pixel at the x,y coordinates to the given value. + * If the coordinates are out of range, do nothing. + */ + virtual void setPixel(unsigned int x, + unsigned int y, + unsigned int pixval); + + /** + * Set a pixel at the x,y coordinates to the given r, g, b values. + * If the coordinates are out of range, do nothing. */ - SioxSegmentator(int w, int h, float *limitsArg, int limitsSize); + virtual void setPixel(unsigned int x, unsigned int y, + unsigned int a, + unsigned int r, + unsigned int g, + unsigned int b); /** - * Destructor + * Get a pixel at the x,y coordinates given. If + * the coordinates are out of range, return 0 */ - virtual ~SioxSegmentator(); + virtual unsigned int getPixel(unsigned int x, unsigned int y); /** - * Segmentates the given image with information from the confidence - * matrix. - *

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

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

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

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

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

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

- * Basically, the method works like the Magic Wand with a - * tolerance threshold of zero. - * - * @param cm confidence matrix to be searched - * @param image image to be searched + * Get a confidence value at the x,y coordinates given. If + * the coordinates are out of range, return 0 + */ + virtual float getConfidence(unsigned int x, unsigned int y); + + /** + * Return the confidence data buffer + */ + virtual float *getConfidenceData(); + + /** + * Return the width of this image + */ + virtual int getWidth(); + + /** + * Return the height of this image + */ + virtual int getHeight(); + + /** + * Saves this image as a simple color PPM */ - void fillColorRegions(float *cm, unsigned long *image); + bool writePPM(const std::string fileName); + + + +#ifdef HAVE_GLIB + + /** + * Special constructor to create an image from a GdkPixbuf. + */ + SioxImage(GdkPixbuf *buf); + + /** + * Creates a GdkPixbuf from this image. The user must + * remember to destroy the image when no longer needed. + * with g_free(pixbuf) + */ + GdkPixbuf *getGdkPixbuf(); + +#endif private: + SioxImage() + {} + /** - * Prevent this from being used + * Assign values to that of another */ - SioxSegmentator(); + void assign(const SioxImage &other); - /** error logging **/ - void error(char *format, ...); + /** + * Initialize values. Used by constructors + */ + void init(unsigned int width, unsigned int height); - /** trace logging **/ - void trace(char *format, ...); + bool valid; - typedef enum - { - ADD_EDGE, /** Add mode for the subpixel refinement. */ - SUB_EDGE /** Subtract mode for the subpixel refinement. */ - } BrushMode; + unsigned int width; - // instance fields: + unsigned int height; - /** Horizontal resolution of the image to be segmentated. */ - int imgWidth; + unsigned long imageSize; - /** Vertical resolution of the image to be segmentated. */ - int imgHeight; + /** + * Pixel data + */ + unsigned int *pixdata; - /** Number of pixels and/or confidence matrix values to process. - equal to imgWidth * imgHeight - */ - long pixelCount; + /** + * Confidence matrix data + */ + float *cmdata; +}; - /** Stores component label (index) by pixel it belongs to. */ - int *labelField; + + + + +//######################################################################## +//# S I O X +//######################################################################## + +/** + * + */ +class Siox +{ +public: + + /** + * Confidence corresponding to a certain foreground region (equals one). + */ + static const float CERTAIN_FOREGROUND_CONFIDENCE; //=1.0f; /** - * LAB color values of pixels that are definitly known background. - * Entries are of form {l,a,b}. + * Confidence for a region likely being foreground. + */ + static const float FOREGROUND_CONFIDENCE; //=0.8f; + + /** + * Confidence for foreground or background type being equally likely. */ - std::vector knownBg; + static const float UNKNOWN_REGION_CONFIDENCE; //=0.5f; /** - * LAB color values of pixels that are definitly known foreground. - * Entries are of form {l,a,b}. + * Confidence for a region likely being background. */ - std::vector knownFg; + static const float BACKGROUND_CONFIDENCE; //=0.1f; - /** Holds background signature (a characteristic subset of the bg.) */ - std::vector bgSignature; + /** + * Confidence corresponding to a certain background reagion (equals zero). + */ + static const float CERTAIN_BACKGROUND_CONFIDENCE; //=0.0f; - /** Holds foreground signature (a characteristic subset of the fg).*/ - std::vector fgSignature; + /** + * Construct a Siox engine + */ + Siox(); - /** Size of cluster on lab axis. */ - float *limits; + /** + * + */ + virtual ~Siox(); + + /** + * Extract the foreground of the original image, according + * to the values in the confidence matrix. If the operation fails, + * sioxImage.isValid() will be false. + * backgroundFillColor is any ARGB color, such as 0xffffff (white) + * or 0x000000 (black) + */ + virtual SioxImage extractForeground(const SioxImage &originalImage, + unsigned int backgroundFillColor); + +private: + + /** + * Our signature limits + */ + float limits[3]; + + /** + * Image width + */ + unsigned int width; + + /** + * Image height + */ + unsigned int height; + + /** + * Image size in pixels + */ + unsigned long pixelCount; + + /** + * Image data + */ + unsigned int *image; + + /** + * Image confidence matrix + */ + float *cm; - /** Maximum distance of two lab values. */ + /** + * Markup for image editing + */ + int *labelField; + + + /** + * Maximum distance of two lab values. + */ float clusterSize; /** - * Stores Tupels for fast access to nearest background/foreground pixels. + * Initialize the Siox engine to its 'pristine' state. + * Performed at the beginning of extractForeground(). */ - std::map hs; + void init(); - /** Size of the biggest blob.*/ - int regionCount; + /** + * Clean up any debris from processing. + */ + void cleanup(); - /** Copy of the original image, needed for detail refinement. */ - long *origImage; - long origImageSize; + /** + * Error logging + */ + void error(char *fmt, ...); + + /** + * Trace logging + */ + void trace(char *fmt, ...); + + /** + * Stage 1 of the color signature work. 'dims' will be either + * 2 for grays, or 3 for colors + */ + void colorSignatureStage1(CLAB *points, + unsigned int leftBase, + unsigned int rightBase, + unsigned int recursionDepth, + unsigned int *clusters, + const unsigned int dims); + + /** + * Stage 2 of the color signature work + */ + void colorSignatureStage2(CLAB *points, + unsigned int leftBase, + unsigned int rightBase, + unsigned int recursionDepth, + unsigned int *clusters, + const float threshold, + const unsigned int dims); + + /** + * Main color signature method + */ + bool colorSignature(const std::vector &inputVec, + std::vector &result, + const unsigned int dims); + + + /** + * + */ + void keepOnlyLargeComponents(float threshold, + double sizeFactorToKeep); + + /** + * + */ + int depthFirstSearch(int startPos, float threshold, int curLabel); + + + /** + * + */ + void fillColorRegions(); + + /** + * Applies the morphological dilate operator. + * + * Can be used to close small holes in the given confidence matrix. + */ + void dilate(float *cm, int xres, int yres); + + /** + * Applies the morphological erode operator. + */ + void erode(float *cm, int xres, int yres); + + /** + * Normalizes the matrix to values to [0..1]. + */ + void normalizeMatrix(float *cm, int cmSize); + + /** + * Multiplies matrix with the given scalar. + */ + void premultiplyMatrix(float alpha, float *cm, int cmSize); + + /** + * Blurs confidence matrix with a given symmetrically weighted kernel. + */ + void smooth(float *cm, int xres, int yres, + float f1, float f2, float f3); + + /** + * Squared Euclidian distance of p and q. + */ + float sqrEuclidianDist(float *p, int pSize, float *q); + + /** + * Squared Euclidian distance of p and q. + */ + float sqrEuclidianDist(const CLAB &p, const CLAB &q); - /** A flag that stores if the segmentation algorithm has already ran.*/ - bool segmentated; }; -} //namespace siox -} //namespace org -#endif /* __SIOX_SEGMENTATOR_H__ */ + + +} // namespace siox +} // namespace org + +#endif /* __SIOX_H__ */ +//######################################################################## +//# E N D O F F I L E +//######################################################################## + + diff --git a/src/trace/trace.cpp b/src/trace/trace.cpp index 81ccb64fa..d40fb89d7 100644 --- a/src/trace/trace.cpp +++ b/src/trace/trace.cpp @@ -33,9 +33,13 @@ #include "siox.h" #include "imagemap-gdk.h" -namespace Inkscape { -namespace Trace { + +namespace Inkscape +{ + +namespace Trace +{ @@ -43,11 +47,13 @@ namespace Trace { /** - * + * Get the selected image. Also check for any SPItems over it, in + * case the user wants SIOX pre-processing. */ SPImage * Tracer::getSelectedSPImage() { + SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (!desktop) { @@ -55,11 +61,13 @@ Tracer::getSelectedSPImage() return NULL; } + Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop); + Inkscape::Selection *sel = sp_desktop_selection(desktop); if (!sel) { char *msg = _("Select an image to trace"); - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, msg); + msgStack->flash(Inkscape::ERROR_MESSAGE, msg); //g_warning(msg); return NULL; } @@ -94,7 +102,7 @@ Tracer::getSelectedSPImage() if (img) //we want only one { char *msg = _("Select only one image to trace"); - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, msg); + msgStack->flash(Inkscape::ERROR_MESSAGE, msg); return NULL; } img = SP_IMAGE(item); @@ -112,7 +120,7 @@ Tracer::getSelectedSPImage() if (!img || sioxShapes.size() < 1) { char *msg = _("Select one image and one or more shapes above it"); - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, msg); + msgStack->flash(Inkscape::ERROR_MESSAGE, msg); return NULL; } return img; @@ -124,7 +132,7 @@ Tracer::getSelectedSPImage() if (!item) { char *msg = _("Select an image to trace"); //same as above - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, msg); + msgStack->flash(Inkscape::ERROR_MESSAGE, msg); //g_warning(msg); return NULL; } @@ -132,7 +140,7 @@ Tracer::getSelectedSPImage() if (!SP_IS_IMAGE(item)) { char *msg = _("Select an image to trace"); - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, msg); + msgStack->flash(Inkscape::ERROR_MESSAGE, msg); //g_warning(msg); return NULL; } @@ -146,36 +154,18 @@ Tracer::getSelectedSPImage() -/** - * - */ -GdkPixbuf * -Tracer::getSelectedImage() -{ - - SPImage *img = getSelectedSPImage(); - if (!img) - return NULL; - - GdkPixbuf *pixbuf = img->pixbuf; - - return pixbuf; - -} - +typedef org::siox::SioxImage SioxImage; +typedef org::siox::Siox Siox; GdkPixbuf * Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf) { + if (!sioxEnabled) + return origPixbuf; //Convert from gdk, so a format we know. By design, the pixel //format in PackedPixelMap is identical to what is needed by SIOX - PackedPixelMap *ppMap = gdkPixbufToPackedPixelMap(origPixbuf); - //We need to create two things: - // 1. An array of long pixel values of ARGB - // 2. A matching array of per-pixel float 'confidence' values - unsigned long *imgBuf = ppMap->pixels; - float *confidenceMatrix = new float[ppMap->width * ppMap->height]; + SioxImage simage(origPixbuf); SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (!desktop) @@ -184,11 +174,13 @@ Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf) return NULL; } + Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop); + Inkscape::Selection *sel = sp_desktop_selection(desktop); if (!sel) { char *msg = _("Select an image to trace"); - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, msg); + msgStack->flash(Inkscape::ERROR_MESSAGE, msg); //g_warning(msg); return NULL; } @@ -200,15 +192,12 @@ Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf) double width = (double)(aImg->bbox.x1 - aImg->bbox.x0); double height = (double)(aImg->bbox.y1 - aImg->bbox.y0); - double iwidth = (double)ppMap->width; - double iheight = (double)ppMap->height; + double iwidth = (double)simage.getWidth(); + double iheight = (double)simage.getHeight(); double iwscale = width / iwidth; double ihscale = height / iheight; - unsigned long cmIndex = 0; - - std::vector arenaItems; std::vector::iterator iter; for (iter = sioxShapes.begin() ; iter!=sioxShapes.end() ; iter++) @@ -224,12 +213,13 @@ Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf) } //g_message("%d arena items\n", arenaItems.size()); - PackedPixelMap *dumpMap = PackedPixelMapCreate(ppMap->width, ppMap->height); + PackedPixelMap *dumpMap = PackedPixelMapCreate( + simage.getWidth(), simage.getHeight()); - for (int row=0 ; rowheight ; row++) + for (int row=0 ; rowbbox.y0) + ihscale * (double) row; - for (int col=0 ; colwidth ; col++) + for (int col=0 ; colbbox.x0) + iwscale * (double)col; @@ -257,26 +247,33 @@ Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf) { //g_message("hit!\n"); dumpMap->setPixelLong(dumpMap, col, row, 0L); - confidenceMatrix[cmIndex] = - org::siox::SioxSegmentator::CERTAIN_FOREGROUND_CONFIDENCE; + simage.setConfidence(col, row, + Siox::UNKNOWN_REGION_CONFIDENCE); } else { dumpMap->setPixelLong(dumpMap, col, row, - ppMap->getPixel(ppMap, col, row)); - confidenceMatrix[cmIndex] = - org::siox::SioxSegmentator::CERTAIN_BACKGROUND_CONFIDENCE; + simage.getPixel(col, row)); + simage.setConfidence(col, row, + Siox::CERTAIN_BACKGROUND_CONFIDENCE); } - cmIndex++; } } + //dumpMap->writePPM(dumpMap, "siox1.ppm"); + dumpMap->destroy(dumpMap); + //## ok we have our pixel buf - org::siox::SioxSegmentator ss(ppMap->width, ppMap->height, NULL, 0); - ss.segmentate(imgBuf, confidenceMatrix, 0, 0.0); + org::siox::Siox sengine; + org::siox::SioxImage result = + sengine.extractForeground(simage, 0xffffff); + if (!result.isValid()) + { + g_warning("Invalid SIOX result"); + return NULL; + } - dumpMap->writePPM(dumpMap, "siox.ppm"); - dumpMap->destroy(dumpMap); + //result.writePPM("siox2.ppm"); /* Free Arena and ArenaItem */ /* @@ -289,14 +286,48 @@ Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf) nr_object_unref((NRObject *) arena); */ - GdkPixbuf *newPixbuf = packedPixelMapToGdkPixbuf(ppMap); - ppMap->destroy(ppMap); - delete confidenceMatrix; + GdkPixbuf *newPixbuf = result.getGdkPixbuf(); return newPixbuf; } +/** + * + */ +GdkPixbuf * +Tracer::getSelectedImage() +{ + + SPImage *img = getSelectedSPImage(); + if (!img) + return NULL; + + GdkPixbuf *pixbuf = img->pixbuf; + if (!pixbuf) + return NULL; + + if (sioxEnabled) + { + GdkPixbuf *sioxPixbuf = sioxProcessImage(img, pixbuf); + if (!sioxPixbuf) + { + g_object_ref(pixbuf); + return pixbuf; + } + else + { + return sioxPixbuf; + } + } + else + { + g_object_ref(pixbuf); + return pixbuf; + } + +} + //######################################################################### @@ -331,12 +362,14 @@ void Tracer::traceThread() return; } + Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop); + Inkscape::Selection *selection = sp_desktop_selection (desktop); if (!SP_ACTIVE_DOCUMENT) { char *msg = _("Trace: No active document"); - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, msg); + msgStack->flash(Inkscape::ERROR_MESSAGE, msg); //g_warning(msg); engine = NULL; return; @@ -353,30 +386,19 @@ void Tracer::traceThread() } GdkPixbuf *pixbuf = img->pixbuf; + g_object_ref(pixbuf); + + pixbuf = sioxProcessImage(img, pixbuf); if (!pixbuf) { char *msg = _("Trace: Image has no bitmap data"); - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, msg); + msgStack->flash(Inkscape::ERROR_MESSAGE, msg); //g_warning(msg); engine = NULL; return; } - //## SIOX pre-processing to get a smart subimage of the pixbuf. - //## This is done before any other filters - if (sioxEnabled) - { - /* - Ok, we have requested siox, and getSelectedSPImage() has found a single - bitmap and one or more SPItems above it. Now what we need to do is create - a siox-segmented subimage pixbuf. We not need alter 'img' at all, since this - pixbuf will be the same dimensions and at the same location. - Remember to free this new pixbuf later. - */ - pixbuf = sioxProcessImage(img, pixbuf); - } - int nrPaths; TracingEngineResult *results = engine->trace(pixbuf, &nrPaths); //printf("nrPaths:%d\n", nrPaths); @@ -467,11 +489,8 @@ void Tracer::traceThread() Inkscape::GC::release(pathRepr); } - //did we allocate a pixbuf copy? - if (sioxEnabled) - { - g_free(pixbuf); - } + //release our pixbuf + g_object_unref(pixbuf); delete results; @@ -489,7 +508,7 @@ void Tracer::traceThread() engine = NULL; char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount); - sp_desktop_message_stack(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg); + msgStack->flash(Inkscape::NORMAL_MESSAGE, msg); g_free(msg); } diff --git a/src/ui/dialog/tracedialog.cpp b/src/ui/dialog/tracedialog.cpp index 9f1241eed..4bfb66f3d 100644 --- a/src/ui/dialog/tracedialog.cpp +++ b/src/ui/dialog/tracedialog.cpp @@ -343,11 +343,12 @@ TraceDialogImpl::TraceDialogImpl() /*#### SIOX ####*/ //# for now, put at the top of the potrace box. something better later - sioxButton.set_label(_("SIOX subimage selection")); + sioxButton.set_label(_("SIOX foreground selection")); sioxBox.pack_start(sioxButton, false, false, MARGIN); - tips.set_tip(sioxButton, _("Subimage selection with the SIOX algorithm")); + tips.set_tip(sioxButton, + _("Cover the area you want to select as the foreground")); sioxVBox.pack_start(sioxBox, false, false, MARGIN); - sioxFrame.set_label(_("SIOX (W.I.P.)")); + sioxFrame.set_label(_("SIOX")); sioxFrame.add(sioxVBox); potraceBox.pack_start(sioxFrame, false, false, 0); -- 2.30.2