siox.cpp revision b411bf4b8d2be4d10c0c5371c3b282639ff47bcf
/*
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
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.h"
#include <math.h>
#include <stdarg.h>
#include <map>
#include <algorithm>
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!
//#########################################
static const int ROOT_TAB_SIZE = 16;
{
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 vx = x / 0.95047;
float vy = y;
float vz = z / 1.08883;
//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
*/
{
float sum=0.0;
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
*/
{
char msgbuf[256];
#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
*/
unsigned int *SioxImage::getImageData()
{
return pixdata;
}
/**
* Set a confidence value at the x,y coordinates to the given value.
* If the coordinates are out of range, do nothing.
*/
void SioxImage::setConfidence(unsigned int x,
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;
*/
float SioxImage::getConfidence(unsigned int x, unsigned int y)
{
{
g_warning("getConfidence: out of bounds (%d,%d)/(%d,%d)",
return 0.0;
}
}
/**
* Return the confidence data buffer
*/
float *SioxImage::getConfidenceData()
{
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];
int alpha = (int)p[3];
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
*/
{
char msgbuf[256];
#ifdef HAVE_GLIB
#else
#endif
}
/**
* Trace logging
*/
{
char msgbuf[256];
#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;
}
{
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());
{
// 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;
}
}
float minFg = 1.0e6f;
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
//########################################################################