/*
Copyright 2005, 2006 by Gerald Friedland, Kristian Jantz and Lars Knipping
Conversion to C++ for Inkscape by Bob Jamison
Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "siox.h"
#include <math.h>
#include <stdarg.h>
#include <map>
#include <algorithm>
#include <cstdlib>
namespace org
{
namespace siox
{
//########################################################################
//# C L A B
//########################################################################
/**
* Convert integer A, R, G, B values into an pixel value.
*/
static unsigned long getRGB(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;
}
/**
* Convert float A, R, G, B values (0.0-1.0) into an pixel value.
*/
static unsigned long getRGB(float a, float r, float g, float b)
{
return getRGB((int)(a * 256.0),
(int)(r * 256.0),
(int)(g * 256.0),
(int)(b * 256.0));
}
//#########################################
//# Root approximations for large speedup.
//# By njh!
//#########################################
{
y = (2.0 * y + x/(y*y))/3.0;
y = (2.0 * y + x/(y*y))/3.0; // polish twice
return y;
}
{
double Y = y*y;
y = (4.0*y + x/(Y*Y))/5.0;
Y = y*y;
y = (4.0*y + x/(Y*Y))/5.0; // polish twice
return y;
}
{
}
static bool _clab_inited_ = false;
{
if (!_clab_inited_)
{
{
}
_clab_inited_ = true;
}
}
/**
* Construct this CieLab from a packed-pixel ARGB value
*/
{
init();
//printf("fr:%f fg:%f fb:%f\n", fr, fg, fb);
if (fr > 0.04045)
//fr = (float) pow((fr + 0.055) / 1.055, 2.4);
else
if (fg > 0.04045)
//fg = (float) pow((fg + 0.055) / 1.055, 2.4);
else
if (fb > 0.04045)
//fb = (float) pow((fb + 0.055) / 1.055, 2.4);
else
// Use white = D65
float vy = y;
//printf("vx:%f vy:%f vz:%f\n", vx, vy, vz);
if (vx > 0.008856)
//vx = (float) pow(vx, 0.3333);
else
if (vy > 0.008856)
//vy = (float) pow(vy, 0.3333);
else
if (vz > 0.008856)
//vz = (float) pow(vz, 0.3333);
else
C = 0;
}
/**
* Return this CieLab's value converted to a packed-pixel ARGB value
*/
{
if (vy3 > 0.008856)
else
if (vx3 > 0.008856)
else
if (vz3 > 0.008856)
else
vz *= 1.08883;
if (vr > 0.0031308)
else
if (vg > 0.0031308)
else
if (vb > 0.0031308)
else
}
/**
* Squared Euclidian distance between this and another color
*/
{
return sum;
}
/**
* Computes squared euclidian distance in CieLab space for two colors
* given as RGB values.
*/
{
return euclid;
}
/**
* Computes squared euclidian distance in CieLab space for two colors
* given as RGB values.
*/
{
}
//########################################################################
//# T U P E L
//########################################################################
/**
* Helper class for storing the minimum distances to a cluster centroid
* in background and foreground and the index to the centroids in each
* signature for a given color.
*/
class Tupel {
public:
Tupel()
{
minBgDist = 0.0f;
indexMinBg = 0;
minFgDist = 0.0f;
indexMinFg = 0;
}
float minFgDistArg, long indexMinFgArg)
{
}
{
}
{
return *this;
}
virtual ~Tupel()
{}
float minBgDist;
long indexMinBg;
float minFgDist;
long indexMinFg;
};
//########################################################################
//# S I O X I M A G E
//########################################################################
/**
* Create an image with the given width and height
*/
{
}
/**
* Copy constructor
*/
{
}
/**
* Assignment
*/
{
return *this;
}
/**
* Clean up after use.
*/
{
}
/**
* Error logging
*/
{
#ifdef HAVE_GLIB
#else
#endif
}
/**
* Returns true if the previous operation on this image
* was successful, else false.
*/
{
return valid;
}
/**
* Sets whether an operation was successful, and whether
* this image should be considered a valid one.
* was successful, else false.
*/
{
}
/**
* Set a pixel at the x,y coordinates to the given value.
* If the coordinates are out of range, do nothing.
*/
unsigned int y,
unsigned int pixval)
{
{
error("setPixel: out of bounds (%d,%d)/(%d,%d)",
return;
}
}
/**
* Set a pixel at the x,y coordinates to the given r, g, b values.
* If the coordinates are out of range, do nothing.
*/
unsigned int a,
unsigned int r,
unsigned int g,
unsigned int b)
{
{
error("setPixel: out of bounds (%d,%d)/(%d,%d)",
return;
}
((r << 16) & 0x00ff0000) |
((g << 8) & 0x0000ff00) |
((b ) & 0x000000ff);
}
/**
* Get a pixel at the x,y coordinates given. If
* the coordinates are out of range, return 0;
*/
{
{
error("getPixel: out of bounds (%d,%d)/(%d,%d)",
return 0L;
}
}
/**
* Return the image data buffer
*/
{
return pixdata;
}
/**
* Set a confidence value at the x,y coordinates to the given value.
* If the coordinates are out of range, do nothing.
*/
unsigned int y,
float confval)
{
{
error("setConfidence: out of bounds (%d,%d)/(%d,%d)",
return;
}
}
/**
* Get a confidence valueat the x,y coordinates given. If
* the coordinates are out of range, return 0;
*/
{
{
g_warning("getConfidence: out of bounds (%d,%d)/(%d,%d)",
return 0.0;
}
}
/**
* Return the confidence data buffer
*/
{
return cmdata;
}
/**
* Return the width of this image
*/
{
return width;
}
/**
* Return the height of this image
*/
{
return height;
}
/**
* Initialize values. Used by constructors
*/
{
valid = true;
for (unsigned long i=0 ; i<imageSize ; i++)
{
pixdata[i] = 0;
cmdata[i] = 0.0;
}
}
/**
* Assign values to that of another
*/
{
for (unsigned long i=0 ; i<imageSize ; i++)
{
}
}
/**
* Write the image to a PPM file
*/
{
if (!f)
return false;
for (unsigned int y=0 ; y<height; y++)
{
for (unsigned int x=0 ; x<width ; x++)
{
//unsigned int alpha = (rgb>>24) & 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
*/
{
if (!buf)
return;
//### Fill in the cells with RGB values
int row = 0;
for (unsigned int y=0 ; y<height ; y++)
{
for (unsigned int x=0 ; x<width ; x++)
{
int r = (int)p[0];
int g = (int)p[1];
int b = (int)p[2];
p += n_channels;
}
}
}
/**
* Create a GdkPixbuf from this image
*/
{
bool has_alpha = true;
if (!pixdata)
return NULL;
//### Fill in the cells with RGB values
int row = 0;
for (unsigned int y=0 ; y < height ; y++)
{
for (unsigned x=0 ; x < width ; x++)
{
if ( n_channels > 3 )
{
}
p += n_channels;
}
}
return buf;
}
#endif /* GLIB */
//########################################################################
//# S I O X
//########################################################################
//##############
//## PUBLIC
//##############
/**
* Confidence corresponding to a certain foreground region (equals one).
*/
/**
* Confidence for a region likely being foreground.
*/
/**
* Confidence for foreground or background type being equally likely.
*/
/**
* Confidence for a region likely being background.
*/
/**
* Confidence corresponding to a certain background reagion (equals zero).
*/
/**
* Construct a Siox engine
*/
sioxObserver(0),
keepGoing(true),
width(0),
height(0),
pixelCount(0),
image(0),
cm(0),
labelField(0)
{
init();
}
/**
* Construct a Siox engine
*/
keepGoing(true),
width(0),
height(0),
pixelCount(0),
image(0),
cm(0),
labelField(0)
{
init();
}
/**
*
*/
{
cleanup();
}
/**
* Error logging
*/
{
#ifdef HAVE_GLIB
#else
#endif
}
/**
* Trace logging
*/
{
#ifdef HAVE_GLIB
#else
#endif
}
/**
* Progress reporting
*/
{
if (!sioxObserver)
return true;
if (!ret)
{
trace("User selected abort");
keepGoing = false;
}
return ret;
}
/**
* Extract the foreground of the original image, according
* to the values in the confidence matrix.
*/
unsigned int backgroundFillColor)
{
trace("### Start");
init();
keepGoing = true;
//# fetch some info from the image
labelField = new int[pixelCount];
trace("### Creating signatures");
//#### create color signatures
for (unsigned long i=0 ; i<pixelCount ; i++)
{
if (conf <= BACKGROUND_CONFIDENCE)
else if (conf >= FOREGROUND_CONFIDENCE)
}
/*
std::vector<CieLab> imageClab;
for (int y = 0 ; y < workImage.getHeight() ; y++)
for (int x = 0 ; x < workImage.getWidth() ; x++)
{
float cm = workImage.getConfidence(x, y);
unsigned int pix = workImage.getPixel(x, y);
CieLab lab(pix);
imageClab.push_back(lab);
if (cm <= BACKGROUND_CONFIDENCE)
knownBg.push_back(lab); //note: uses CieLab(rgb)
else if (cm >= FOREGROUND_CONFIDENCE)
knownFg.push_back(lab);
}
*/
if (!progressReport(10.0))
{
error("User aborted");
delete[] imageClab;
delete[] labelField;
return workImage;
}
trace("knownBg:%u knownFg:%u", static_cast<unsigned int>(knownBg.size()), static_cast<unsigned int>(knownFg.size()));
{
error("Could not create background signature");
delete[] imageClab;
delete[] labelField;
return workImage;
}
if (!progressReport(30.0))
{
error("User aborted");
delete[] imageClab;
delete[] labelField;
return workImage;
}
{
error("Could not create foreground signature");
delete[] imageClab;
delete[] labelField;
return workImage;
}
//trace("### bgSignature:%d", bgSignature.size());
if (bgSignature.empty())
{
// segmentation impossible
error("Signature size is < 1. Segmentation is impossible");
delete[] imageClab;
delete[] labelField;
return workImage;
}
if (!progressReport(30.0))
{
error("User aborted");
delete[] imageClab;
delete[] labelField;
return workImage;
}
// classify using color signatures,
// classification cached in hashmap for drb and speedup purposes
trace("### Analyzing image");
for (unsigned int i=0; i<pixelCount; i++)
{
if (i % progressResolution == 0)
{
float progress =
//trace("### progress:%f", progress);
if (!progressReport(progress))
{
error("User aborted");
delete[] imageClab;
delete[] labelField;
return workImage;
}
}
if (cm[i] >= FOREGROUND_CONFIDENCE)
{
}
else if (cm[i] <= BACKGROUND_CONFIDENCE)
{
}
else // somewhere in between
{
bool isBackground = true;
{
}
else
{
int minIndex = 0;
{
if (d<minBg)
{
minBg = d;
minIndex = j;
}
}
minIndex = -1;
for (unsigned int j = 0 ; j < fgSignature.size() ; j++)
{
if (d < minFg)
{
minFg = d;
minIndex = j;
}
}
if (fgSignature.empty())
{
// remove next line to force behaviour of old algorithm
//error("foreground signature does not exist");
//delete[] labelField;
//workImage.setValid(false);
//return workImage;
}
else
{
}
}
if (isBackground)
else
}
}
delete[] imageClab;
trace("### postProcessing");
//#### postprocessing
//for (int i=0; i < 2/*smoothness*/; i++)
// smooth(cm, width, height, 0.333f, 0.333f, 0.333f); // average
for (unsigned int i=0; i < pixelCount; i++)
{
if (cm[i] >= UNKNOWN_REGION_CONFIDENCE)
else
}
if (!progressReport(100.0))
{
error("User aborted");
delete[] labelField;
return workImage;
}
//#### We are done. Now clear everything but the background
for (unsigned long i = 0; i<pixelCount ; i++)
{
if (conf < FOREGROUND_CONFIDENCE)
image[i] = backgroundFillColor;
}
delete[] labelField;
trace("### Done");
keepGoing = false;
return workImage;
}
//##############
//## PRIVATE
//##############
/**
* Initialize the Siox engine to its 'pristine' state.
* Performed at the beginning of extractForeground().
*/
{
limits[0] = 0.64f;
float negLimits[3];
}
/**
* Clean up any debris from processing.
*/
{
}
/**
* Stage 1 of the color signature work. 'dims' will be either
* 2 for grays, or 3 for colors
*/
unsigned int leftBase,
unsigned int rightBase,
unsigned int recursionDepth,
unsigned int *clusterCount,
const unsigned int dims)
{
{
}
//Do the Rubner-rule split (sounds like a dance)
{
//# partition points according to the dimension
while (true)
{
while ( true )
{
break;
left++;
}
while ( true )
{
break;
right--;
}
break;
left++;
right--;
}
//# Recurse and create sub-trees
}
else
{
//create a leaf
{
}
//printf("clusters:%d\n", *clusters);
if (newpoint.C != 0)
(*clusterCount)++;
}
}
/**
* Stage 2 of the color signature work
*/
unsigned int leftBase,
unsigned int rightBase,
unsigned int recursionDepth,
unsigned int *clusterCount,
const float threshold,
const unsigned int dims)
{
{
}
//Do the Rubner-rule split (sounds like a dance)
{
//# partition points according to the dimension
while (true)
{
while ( true )
{
break;
left++;
}
while ( true )
{
break;
right--;
}
break;
left++;
right--;
}
//# Recurse and create sub-trees
}
else
{
//### Create a leaf
unsigned int sum = 0;
{
if (scale != 0.0)
(*clusterCount)++;
}
}
}
/**
* Main color signature method
*/
const unsigned int dims)
{
return true;
if (!input)
{
error("Could not allocate buffer for signature");
return false;
}
for (unsigned int i=0 ; i < length ; i++)
{
}
unsigned int stage1length = 0;
unsigned int stage2length = 0;
for (unsigned int i=0 ; i < stage2length ; i++)
{
}
delete[] input;
return true;
}
/**
*
*/
double sizeFactorToKeep)
{
int curlabel = 0;
int maxregion= 0;
int maxblob = 0;
// slow but easy to understand:
for (unsigned long i=0 ; i<pixelCount ; i++)
{
int regionCount = 0;
{
}
if (regionCount>maxregion)
{
}
}
for (unsigned int i=0 ; i<pixelCount ; i++)
{
if (labelField[i] != -1)
{
// remove if the component is to small
// add maxblob always to foreground
if (labelField[i] == maxblob)
}
}
}
{
// stores positions of labeled pixels, where the neighbours
// should still be checked for processing:
//trace("startPos:%d threshold:%f curLabel:%d",
// startPos, threshold, curLabel);
int componentSize = 0;
{
}
while (!pixelsToVisit.empty())
{
// check all four neighbours
{
}
{
}
{
}
{
}
}
return componentSize;
}
/**
*
*/
void Siox::fillColorRegions()
{
//int maxRegion=0; // unused now
for (unsigned long i=0; i<pixelCount; i++)
{ // for all pixels
{
continue; // already visited or bg
}
unsigned long curLabel = i+1;
labelField[i] = curLabel;
// int componentSize = 1;
// depth first search to fill region
while (!pixelsToVisit.empty())
{
// check all four neighbours
{
// ++componentSize;
}
{
// ++componentSize;
}
{
// ++componentSize;
}
{
// ++componentSize;
}
}
//if (componentSize>maxRegion) {
// maxRegion=componentSize;
//}
}
}
/**
* Applies the morphological dilate operator.
*
* Can be used to close small holes in the given confidence matrix.
*/
{
for (int y=0; y<yres; y++)
{
for (int x=0; x<xres-1; x++)
{
}
}
for (int y=0; y<yres; y++)
{
{
}
}
for (int y=0; y<yres-1; y++)
{
for (int x=0; x<xres; x++)
{
}
}
{
for (int x=0; x<xres; x++)
{
}
}
}
/**
* Applies the morphological erode operator.
*/
{
for (int y=0; y<yres; y++)
{
for (int x=0; x<xres-1; x++)
{
}
}
for (int y=0; y<yres; y++)
{
{
}
}
for (int y=0; y<yres-1; y++)
{
for (int x=0; x<xres; x++)
{
}
}
{
for (int x=0; x<xres; x++)
{
}
}
}
/**
* Normalizes the matrix to values to [0..1].
*/
{
float max= -1000000.0f;
for (int i=0; i<cmSize; i++)
//good to use STL, but max() is not iterative
//float max = *std::max(cm, cm + cmSize);
return;
}
/**
* Multiplies matrix with the given scalar.
*/
{
for (int i=0; i<cmSize; i++)
}
/**
* 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.
*/
{
for (int y=0; y<yres; y++)
{
for (int x=0; x<xres-2; x++)
{
}
}
for (int y=0; y<yres; y++)
{
{
}
}
for (int y=0; y<yres-2; y++)
{
for (int x=0; x<xres; x++)
{
}
}
{
for (int x=0; x<xres; x++)
{
}
}
}
/**
* Squared Euclidian distance of p and q.
*/
{
float sum=0.0;
for (int i=0; i<pSize; i++)
{
float v = p[i] - q[i];
sum += v*v;
}
return sum;
}
} // namespace siox
} // namespace org
//########################################################################
//# E N D O F F I L E
//########################################################################