siox.cpp revision 18986998321c18bfee1b2656b74c33b456c80fdd
/*
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-segmentator.h"
#include <string>
#include <stdarg.h> //for error() and trace()
#include <math.h> //sqrt(), pow(), round(), etc
namespace org
{
namespace siox
{
//########################################################################
//## U T I L S (originally Utils.java)
//########################################################################
/**
* Collection of auxiliary image processing methods used by the
* SioxSegmentator mainly for postprocessing.
*
* @author G. Friedland, K. Jantz, L. Knipping
* @version 1.05
*
* Conversion to C++ by Bob Jamison
*
*/
/** Caches color conversion values to speed up RGB->CIELAB conversion.*/
//forward decls
//static float colordiffsq(long rgb0, long rgb1);
//static int getAlpha(long argb);
//static long packPixel(int a, int r, int g, int b);
/**
* 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.
*/
{
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.
*
* @param cm Confidence matrix to be processed.
* @param xres Horizontal resolution of the matrix.
* @param yres Vertical resolution of the 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++) {
}
}
}
/**
* Normalizes the matrix to values to [0..1].
*
* @param cm The matrix to be normalized.
*/
{
float max=0.0f;
for (int i=0; i<cmSize; i++) {
}
if (max<=0.0)
return;
else if (max==1.00)
return;
}
/**
* Multiplies matrix with the given scalar.
*
* @param alpha The scalar value.
* @param cm The matrix of values be multiplied with alpha.
* @param cmSize The matrix length.
*/
{
for (int i=0; i<cmSize; i++)
}
/**
* Blurs confidence matrix with a given symmetrically weighted kernel.
* <P>
* In the standard case confidence matrix entries are between 0...1 and
* the weight factors sum up to 1.
*
* @param cm The matrix to be smoothed.
* @param xres Horizontal resolution of the matrix.
* @param yres Vertical resolution of the matrix.
* @param f1 Weight factor for the first pixel.
* @param f2 Weight factor for the mid-pixel.
* @param f3 Weight factor for the last pixel.
*/
{
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.
* <P>
* Usage hint: When only comparisons between Euclidian distances without
* actual values are needed, the squared distance can be preferred
* for being faster to compute.
*
* @param p First euclidian point coordinates.
* @param pSize Length of coordinate array.
* @param q Second euclidian point coordinates.
* Dimension must not be smaller than that of p.
* Any extra dimensions will be ignored.
* @return Squared euclidian distance of p and q.
* @see #euclid
*/
static float sqrEuclidianDist(float *p, int pSize, float *q)
{
float sum=0;
for (int i=0; i<pSize; i++)
sum+=(p[i]-q[i])*(p[i]-q[i]);
return sum;
}
/**
* Squared Euclidian distance of p and q.
* <P>
* Usage hint: When only comparisons between Euclidian distances without
* actual values are needed, the squared distance can be preferred
* for being faster to compute.
*
* @param p CLAB value
* @param q second CLAB value
* @return Squared euclidian distance of p and q.
* @see #euclid
*/
{
float sum=0;
sum += (p.C - q.C) * (p.C - q.C);
sum += (p.L - q.L) * (p.L - q.L);
sum += (p.A - q.A) * (p.A - q.A);
sum += (p.B - q.B) * (p.B - q.B);
return sum;
}
/**
* Euclidian distance of p and q.
*
* @param p First euclidian point coordinates.
* @param pSize Length of coordinate array.
* @param q Second euclidian point coordinates.
* Dimension must not be smaller than that of p.
* Any extra dimensions will be ignored.
* @return Squared euclidian distance of p and q.
* @see #sqrEuclidianDist
*/
/*
static float euclid(float *p, int pSize, float *q)
{
return (float)sqrt(sqrEuclidianDist(p, pSize, q));
}
*/
/**
* Computes Euclidian distance of two RGB color values.
*
* @param rgb0 First color value.
* @param rgb1 Second color value.
* @return Euclidian distance between the two color values.
*/
/*
static float colordiff(long rgb0, long rgb1)
{
return (float)sqrt(colordiffsq(rgb0, rgb1));
}
*/
/**
* Computes squared euclidian distance of two RGB color values.
* <P>
* Note: Faster to compute than colordiff
*
* @param rgb0 First color value.
* @param rgb1 Second color value.
* @return Squared Euclidian distance between the two color values.
*/
/*
static float colordiffsq(long rgb0, long rgb1)
{
int rDist=getRed(rgb0) - getRed(rgb1);
int gDist=getGreen(rgb0) - getGreen(rgb1);
int bDist=getBlue(rgb0) - getBlue(rgb1);
return (float)(rDist*rDist+gDist*gDist+bDist*bDist);
}
*/
/**
* Averages two ARGB colors.
*
* @param argb0 First color value.
* @param argb1 Second color value.
* @return The averaged ARGB color.
*/
/*
static long average(long argb0, long argb1)
{
long ret = packPixel(
(getAlpha(argb0) + getAlpha(argb1))/2,
(getRed(argb0) + getRed(argb1) )/2,
(getGreen(argb0) + getGreen(argb1))/2,
(getBlue(argb0) + getBlue(argb1) )/2);
return ret;
}
*/
/**
* Computes squared euclidian distance in CLAB space for two colors
* given as RGB values.
*
* @param rgb0 First color value.
* @param rgb1 Second color value.
* @return Squared Euclidian distance in CLAB space.
*/
{
float euclid=0.0f;
return euclid;
}
/**
* Computes squared euclidian distance in CLAB space for two colors
* given as RGB values.
*
* @param rgb0 First color value.
* @param rgb1 Second color value.
* @return Euclidian distance in CLAB space.
*/
{
}
/**
* Converts 24-bit RGB values to {l,a,b} float values.
* <P>
* The conversion used is decribed at
* <a href="http://www.easyrgb.com/math.php?MATH=M7#text7">CLAB Conversion</a>
* for reference white D65. Note that that the conversion is computational
* expensive. Result are cached to speed up further conversion calls.
*
* @param rgb RGB color value,
* @return CLAB color value tripel.
*/
{
{
return res;
}
if (var_R>0.04045)
else
if (var_G>0.04045)
else
if (var_B>0.04045)
else
// Observer. = 2�, Illuminant = D65
float var_X=X/95.047f;
float var_Y=Y/100.0f;
float var_Z=Z/108.883f;
if (var_X>0.008856f)
else
if (var_Y>0.008856f)
else
if (var_Z>0.008856f)
else
return lab;
}
/**
* Converts an CLAB value to a RGB color value.
* <P>
* This is the reverse operation to rgbToClab.
* @param clab CLAB value.
* @return RGB value.
* @see #rgbToClab
*/
/*
static long clabToRGB(const CLAB &clab)
{
float L=clab.L;
float a=clab.A;
float b=clab.B;
float var_Y=(L+16.0f)/116.0f;
float var_X=a/500.0f+var_Y;
float var_Z=var_Y-b/200.0f;
float var_yPow3=(float)pow(var_Y, 3.0);
float var_xPow3=(float)pow(var_X, 3.0);
float var_zPow3=(float)pow(var_Z, 3.0);
if (var_yPow3>0.008856f)
var_Y=var_yPow3;
else
var_Y=(var_Y-16.0f/116.0f)/7.787f;
if (var_xPow3>0.008856f)
var_X=var_xPow3;
else
var_X=(var_X-16.0f/116.0f)/7.787f;
if (var_zPow3>0.008856f)
var_Z=var_zPow3;
else
var_Z=(var_Z-16.0f/116.0f)/7.787f;
float X=95.047f * var_X; //ref_X= 95.047 Observer=2�, Illuminant=D65
float Y=100.0f * var_Y; //ref_Y=100.000
float Z=108.883f * var_Z; //ref_Z=108.883
var_X=X/100.0f; //X = From 0 to ref_X
var_Y=Y/100.0f; //Y = From 0 to ref_Y
var_Z=Z/100.0f; //Z = From 0 to ref_Y
float var_R=(float)(var_X * 3.2406f + var_Y * -1.5372f + var_Z * -0.4986f);
float var_G=(float)(var_X * -0.9689f + var_Y * 1.8758f + var_Z * 0.0415f);
float var_B=(float)(var_X * 0.0557f + var_Y * -0.2040f + var_Z * 1.0570f);
if (var_R>0.0031308f)
var_R=(float)(1.055f*pow(var_R, (1.0f/2.4f))-0.055f);
else
var_R=12.92f*var_R;
if (var_G>0.0031308f)
var_G=(float)(1.055f*pow(var_G, (1.0f/2.4f))-0.055f);
else
var_G=12.92f*var_G;
if (var_B>0.0031308f)
var_B=(float)(1.055f*pow(var_B, (1.0f/2.4f))-0.055f);
else
var_B=12.92f*var_B;
int R = (int)lround(var_R*255.0f);
int G = (int)lround(var_G*255.0f);
int B = (int)lround(var_B*255.0f);
return packPixel(0xFF, R, G, B);
}
*/
/**
* Sets the alpha byte of a pixel.
*
* Constructs alpha to values from 0 to 255.
* @param alpha Alpha value from 0 (transparent) to 255 (opaque).
* @param rgb The 24bit rgb color to be combined with the alpga value.
* @return An ARBG calor value.
*/
{
if (alpha>255)
alpha=0;
else if (alpha<0)
alpha=0;
}
/**
* 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.
*/
{
}
/**
* Limits the values of a,r,g,b to values from 0 to 255 and puts them
* together into an 32 bit integer.
*
* @param a Alpha part, the first byte.
* @param r Red part, the second byte.
* @param g Green part, the third byte.
* @param b Blue part, the fourth byte.
* @return A ARBG value packed to an int.
*/
/*
static long packPixel(int a, int r, int g, int b)
{
if (a<0)
a=0;
else if (a>255)
a=255;
if (r<0)
r=0;
else if (r>255)
r=255;
if (g<0)
g=0;
else if (g>255)
g=255;
if (b<0)
b=0;
else if (b>255)
b=255;
return (a<<24)|(r<<16)|(g<<8)|b;
}
*/
/**
* Returns the alpha component of an ARGB value.
*
* @param argb An ARGB color value.
* @return The alpha component, ranging from 0 to 255.
*/
/*
static int getAlpha(long argb)
{
return (argb>>24)&0xFF;
}
*/
/**
* Returns the red component of an (A)RGB value.
*
* @param rgb An (A)RGB color value.
* @return The red component, ranging from 0 to 255.
*/
{
}
/**
* 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.
*/
{
}
/**
* 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.
*/
{
return (rgb)&0xFF;
}
/**
* Returns a string representation of a CLAB value.
*
* @param clab The CLAB value.
* @param clabSize Size of the CLAB value.
* @return A string representation of the CLAB value.
*/
/*
static std::string clabToString(const CLAB &clab)
{
std::string buff;
char nbuf[60];
snprintf(nbuf, 59, "%5.3f, %5.3f, %5.3f", clab.L, clab.A, clab.B);
buff = nbuf;
return buff;
}
*/
//########################################################################
//## C O L O R S I G N A T U R E (originally ColorSignature.java)
//########################################################################
/**
* Representation of a color signature.
* <br><br>
* This class implements a clustering algorithm based on a modified kd-tree.
* The splitting rule is to simply divide the given interval into two equally
* sized subintervals.
* In the <code>stageone()</code>, approximate clusters are found by building
* up such a tree and stopping when an interval at a node has become smaller
* than the allowed cluster diameter, which is given by <code>limits</code>.
* At this point, clusters may be split in several nodes.<br>
* Therefore, in <code>stagetwo()</code>, nodes that belong to several clusters
* are recombined by another k-d tree clustering on the prior cluster
* centroids. To guarantee a proper level of abstraction, clusters that contain
* less than 0.01% of the pixels of the entire sample are removed. Please
* refer to the file NOTICE to get links to further documentation.
*
* @author Gerald Friedland, Lars Knipping
* @version 1.02
*
* Conversion to C++ by Bob Jamison
*
*/
/**
* Stage one of clustering.
* @param points float[][] the input points in LAB space
* @param depth int used for recursion, start with 0
* @param clusters ArrayList an Arraylist to store the clusters
* @param limits float[] the cluster diameters
*/
int depth,
float *limits)
{
return;
int dims=3;
float min = 0.0f;
float max = 0.0f;
if (curdim == 0)
{
// find maximum and minimum
}
}
else if (curdim == 1)
{
// find maximum and minimum
}
}
else if (curdim == 2)
{
// find maximum and minimum
}
}
else if (curdim == 3)
{
// find maximum and minimum
}
}
// split
float v = 0.0f;
if (curdim==0)
v = points[i].C;
else if (curdim==1)
v = points[i].L;
else if (curdim==2)
v = points[i].A;
else if (curdim==3)
v = points[i].B;
if (v <= pivotvalue) {
} else {
}
} // create subtrees
} else { // create leave
}
}
/**
* Stage two of clustering.
* @param points float[][] the input points in LAB space
* @param depth int used for recursion, start with 0
* @param clusters ArrayList an Arraylist to store the clusters
* @param limits float[] the cluster diameters
* @param total int the total number of points as given to stageone
* @param threshold should be 0.01 - abstraction threshold
*/
int depth,
{
return;
float min = 0.0f;
float max = 0.0f;
if (curdim == 0)
{
// find maximum and minimum
}
}
else if (curdim == 1)
{
// find maximum and minimum
}
}
else if (curdim == 2)
{
// find maximum and minimum
}
}
// split
float v = 0.0f;
if (curdim==0)
v = points[i].L;
else if (curdim==1)
v = points[i].A;
else if (curdim==2)
v = points[i].B;
if (v <= pivotvalue) {
} else {
}
} // create subtrees
} else { // create leave
float sum=0.0;
}
}
}
}
}
/**
* 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 res;
}
//########################################################################
//## S I O X S E G M E N T A T O R (originally SioxSegmentator.java)
//########################################################################
//### NOTE: Doxygen comments are in siox-segmentator.h
{
imgWidth = w;
imgHeight = h;
if (!limitsArg) {
limits = new float[3];
limits[0] = 0.64f;
} else {
limits = new float[limitsSize];
for (int i=0 ; i<limitsSize ; i++)
}
float negLimits[3];
segmentated=false;
}
{
delete labelField;
delete limits;
delete origImage;
}
/**
* Error logging
*/
{
}
/**
* Trace logging
*/
{
}
int smoothness, double sizeFactorToKeep)
{
segmentated=false;
// save image for drb
for (int i=0 ; i<imageSize ; i++)
// create color signatures
for (int i=0; i<cmSize; i++) {
if (cm[i]<=BACKGROUND_CONFIDENCE)
else if (cm[i]>=FOREGROUND_CONFIDENCE)
}
// segmentation impossible
return false;
}
// classify using color signatures,
// classification cached in hashmap for drb and speedup purposes
for (int i=0; i<cmSize; i++) {
if (cm[i]>=FOREGROUND_CONFIDENCE) {
continue;
}
if (cm[i]>BACKGROUND_CONFIDENCE) {
bool isBackground=true;
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.size()==0) {
// remove next line to force behaviour of old algorithm
error("foreground signature does not exist");
return false;
} else {
}
} else {
}
if (isBackground) {
} else {
}
} else {
}
}
// postprocessing
for (int i=0; i<smoothness; i++) {
}
for (int i=0; i<cmSize; i++) {
if (cm[i]>=UNKNOWN_REGION_CONFIDENCE) {
} else {
}
}
segmentated=true;
return true;
}
float threshold,
double sizeFactorToKeep)
{
int idx = 0;
for (int i=0 ; i<imgHeight ; i++)
for (int j=0 ; j<imgWidth ; j++)
int curlabel = 0;
int maxregion= 0;
int maxblob = 0;
// slow but easy to understand:
for (int i=0 ; i<cmSize ; i++) {
regionCount=0;
}
if (regionCount>maxregion) {
}
}
for (int i=0 ; i<cmSize ; 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:
int componentSize=0;
labelField[i] = curLabel;
}
while (pixelsToVisit.size() > 0) {
// check all four neighbours
}
}
}
}
}
return componentSize;
}
{
}
int brushmode,
{
if (!segmentated) {
error("no segmentation yet");
return false;
}
/* we are using a rect, not necessary
if (!area.contains(ex, ey)) {
continue;
}
*/
float minDistBg = 0.0f;
float minDistFg = 0.0f;
} else {
continue;
}
float alpha;
if (minDistFg==0) {
} else {
}
}
}
// foreground, we want to take something away
float alpha;
if (minDistBg==0) {
} else {
// bg = gf -> 1
// more fg -> <1
}
}
}
} else {
return false;
}
}
}
return true;
}
{
int idx = 0;
for (int i=0 ; i<imgHeight ; i++)
for (int j=0 ; i<imgWidth ; j++)
//int maxRegion=0; // unused now
for (int i=0; i<cmSize; i++) { // for all pixels
continue; // already visited or bg
}
int curLabel=i+1;
labelField[i]=curLabel;
// int componentSize = 1;
// depth first search to fill region
while (pixelsToVisit.size() > 0) {
// check all four neighbours
// ++componentSize;
}
// ++componentSize;
}
// ++componentSize;
}
// ++componentSize;
}
}
//if (componentSize>maxRegion) {
// maxRegion=componentSize;
//}
}
}
} //namespace siox
} //namespace org