package fr.unistra.pelican.algorithms.segmentation;
import fr.unistra.pelican.Algorithm;
import fr.unistra.pelican.AlgorithmException;
import fr.unistra.pelican.DoubleImage;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.IntegerImage;
/**
*
* Given a colour image in a polar colour space, this class performs a hue
* clustering.
*
* It is based on the statistics.MultipleReferenceHues process.
*
* @author Lefevre
*
*/
public class HueClassification extends Algorithm {
/**
* the input image
*/
public Image input;
/**
* the threshold (ratio of the average)
*/
public double threshold=1.0;
/**
* the labeled image
*/
public Image output;
/**
* Given a colour image in a polar colour space, this class performs a hue
* clustering
*
* @param input
* the input image
* @return the reference hues
*/
public static Image exec(Image input) {
return (Image) new HueClassification().process(input);
}
public static Image exec(Image input,double threshold) {
return (Image) new HueClassification().process(input,threshold);
}
/**
* Constructor
*
*/
public HueClassification() {
super.inputs = "input";
super.outputs = "output";
super.options="threshold";
}
/*
* (non-Javadoc)
*
* @see fr.unistra.pelican.Algorithm#launch()
*/
public void launch() throws AlgorithmException {
// compute its normalised histogram...over 360 bins
double[] histoOriginal = new double[360];
double[] histoTmp = new double[360];
int[] labels = new int[360];
for (int x = 0; x < input.getXDim(); x++) {
for (int y = 0; y < input.getYDim(); y++) {
double sat = input.getPixelXYBDouble(x, y, 1);
double coeff = 1 / (1 + Math.exp(-5.0 * (sat - 0.5)));
histoOriginal[(int) Math
.floor(360.0 * input.getPixelXYBDouble(x, y, 0))] += coeff;
}
}
// normalize
for (int i = 0; i < 360; i++)
histoOriginal[i] = histoOriginal[i] / input.size();
// set histo as image
Image tmp = new DoubleImage(360, 1, 1, 1, 1);
for (int x = 0; x < 360; x++)
tmp.setPixelXYDouble(x, 0, histoOriginal[x]);
//BooleanImage se = FlatStructuringElement2D.createHorizontalLineFlatStructuringElement(10);
// close it
// tmp = (Image) new GrayClosing().process(tmp, se);
// take back the values
for (int x = 0; x < 360; x++)
histoTmp[x] = tmp.getPixelXYDouble(x, 0);
// get its average
double avg = 0.0;
for (int x = 0; x < 360; x++)
avg += histoTmp[x];
avg /= 360.0;
avg *= threshold;
System.err.println("Average hue:" + avg);
// threshold using its average
for (int x = 0; x < 360; x++)
if (histoTmp[x] < avg)
histoTmp[x] = 0.0;
// get CCs and dont forget to connect the extrema of the hue circle
Section[] sections = new Section[180]; // max CC number
int j = 0;
boolean flag = false;
for (int x = 0; x < 360; x++) {
if (histoTmp[x] > 0.0) {
flag = false;
if (sections[j] == null) {
sections[j] = new Section(x, x);
sections[j].sum += histoOriginal[x];
} else {
sections[j].end++;
sections[j].sum += histoOriginal[x];
}
} else {
if (flag == false && sections[j] != null) {
j++;
flag = true;
}
}
}
// index of the last section
for (int last = 0; sections[last] != null; last++)
// if the first and last sections are connected add them to the last
if (sections[0].start == 0 && sections[last].end == 359) {
sections[last].end = sections[0].end;
sections[last].sum += sections[0].sum;
sections[0] = sections[last];
sections[last] = null;
}
for (int k = 0; sections[k] != null; k++)
System.err.println(sections[k].start + " " + sections[k].end + " "
+ sections[k].sum);
output = new IntegerImage(input.getXDim(), input.getYDim(), 1, 1, 1);
// build the LUT
for (int i = 0; sections[i] != null; i++) {
for (int k = sections[i].start; k < sections[i].end; k++)
labels[k] = i + 1;
if (sections[i].start > sections[i].end) {
for (int k = sections[i].start; k < 360; k++)
labels[k] = i + 1;
for (int k = 0; k < sections[i].end; k++)
labels[k] = i + 1;
}
}
// label the image
for (int x = 0; x < input.getXDim(); x++)
for (int y = 0; y < input.getYDim(); y++)
output.setPixelXYInt(x, y, labels[(int) Math.floor(360.0 * input
.getPixelXYBDouble(x, y, 0))]);
}
private class Section {
int start;
int end;
double sum;
Section(int start, int end) {
this.start = start;
this.end = end;
this.sum = 0.0;
}
}
}