package fr.unistra.pelican.algorithms.segmentation;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.TreeSet;
import fr.unistra.pelican.Algorithm;
import fr.unistra.pelican.AlgorithmException;
import fr.unistra.pelican.BooleanImage;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.InvalidNumberOfParametersException;
import fr.unistra.pelican.InvalidTypeOfParameterException;
import fr.unistra.pelican.algorithms.applied.remotesensing.RegionBuilderWatershedClassical;
import fr.unistra.pelican.algorithms.io.ImageLoader;
import fr.unistra.pelican.algorithms.io.RegionsLoader;
import fr.unistra.pelican.algorithms.segmentation.labels.DrawFrontiersOnImage;
import fr.unistra.pelican.algorithms.visualisation.Viewer2D;
import fr.unistra.pelican.util.morphology.FlatStructuringElement2D;
/**
* Evaluate a segmentation given a expert segmentation. The expert segmentation
* can be multi-band.
*
* @author Sebastien Derivaux
*/
public class EvalSegmentation extends Algorithm {
/*
* Input Image
*/
public Image input;
/*
* Expert Image
*/
public Image expert;
/*
* Result evaluation
*/
public String result;
// BIG HACK
public static double eval;
public static double maxTheoricalAccuracy;
public static double[] overSegmentation;
/**
* Constructor
*
*/
public EvalSegmentation() {
super();
super.inputs = "input,expert";
super.outputs = "result";
}
/*
* (non-Javadoc)
*
* @see fr.unistra.pelican.Algorithm#launch()
*/
public void launch() throws AlgorithmException {
int xDim = input.getXDim();
int yDim = input.getYDim();
int nbClasses = expert.getBDim();
double overSegmentationGlobal = 0.0;
double overSegmentationPond = 0.0;
int nbLabelsTotal = 0;
overSegmentation = new double[nbClasses];
// Calculate oversegmentation.
for (int c = 0; c < nbClasses; c++) {
int nbLabels = 0;
// Count the number of labels in the expert band.
for (int x = 0; x < xDim; x++)
for (int y = 0; y < yDim; y++)
nbLabels = Math.max(expert.getPixelXYBInt(x, y, c),
nbLabels);
// Don't forget the label 0 is background not an effective label.
LinkedList<Integer> segments = new LinkedList<Integer>();
for (int x = 0; x < xDim; x++)
for (int y = 0; y < yDim; y++) {
int expertLabel = expert.getPixelXYBInt(x, y, c);
int segment = input.getPixelXYInt(x, y);
if (expertLabel != 0)
if (!segments.contains(segment))
segments.add(segment);
}
int nbSegments = segments.size();
overSegmentation[c] = (double) nbSegments / (double) nbLabels;
overSegmentationGlobal += overSegmentation[c];
overSegmentationPond += overSegmentation[c] * nbLabels;
nbLabelsTotal += nbLabels;
}
overSegmentationGlobal /= nbClasses;
overSegmentationPond /= (double) nbLabelsTotal;
result = "oversegmentation per class = "
+ Arrays.toString(overSegmentation) + " \n"
+ "medium oversegmentation (per class) = "
+ overSegmentationGlobal + " \n"
+ "medium oversegmentation (per label) = "
+ overSegmentationPond + "\n";
// Calculate undersegmentation and minimum classification error per
// regions.
// There may be void segments.
int nbSegments = 0;
for (int x = 0; x < xDim; x++)
for (int y = 0; y < yDim; y++)
nbSegments = Math.max(input.getPixelXYInt(x, y), nbSegments);
nbSegments++; // Don't forget segment 0
TreeSet<Integer>[] set = new TreeSet[nbSegments];
int[][] distribution = new int[nbSegments][];
for (int i = 0; i < nbSegments; i++) {
set[i] = new TreeSet<Integer>();
distribution[i] = new int[nbClasses];
}
for (int x = 0; x < xDim; x++)
for (int y = 0; y < yDim; y++) {
for (int c = 0; c < nbClasses; c++)
if (expert.getPixelXYBInt(x, y, c) != 0) {
set[input.getPixelXYInt(x, y)].add(c);
distribution[input.getPixelXYInt(x, y)][c]++;
}
}
int nbSegmentWithLabel = 0;
int nbLabels = 0;
int nbPixelExpert = 0;
int nbPixelClassed = 0;
for (int i = 0; i < nbSegments; i++) {
int count = set[i].size();
if (count > 0) {
nbSegmentWithLabel++;
nbLabels += count;
}
int max = 0;
for (int c = 0; c < nbClasses; c++) {
nbPixelExpert += distribution[i][c];
max = Math.max(max, distribution[i][c]);
}
nbPixelClassed += max;
}
result += "Average number of labels per segments = "
+ (double) nbLabels / (double) nbSegmentWithLabel + " \n";
result += "Maximum precision for pixel classification = "
+ (double) nbPixelClassed / (double) nbPixelExpert + " \n";
double mppc = ((double) nbPixelClassed / (double) nbPixelExpert);
maxTheoricalAccuracy = mppc;
eval = 1.0
/ (overSegmentationPond >= 1.0 ? overSegmentationPond : 1.0)
* (mppc > 0.95 ? mppc
: (mppc > 0.90 ? mppc * 0.5 : mppc * 0.01)) * 20.0;
}
/*
* Evaluate a segmentation.
*/
public String exec(Image input, Image expert) {
return (String)new EvalSegmentation().process(input, expert);
}
/*
public static void main(String[] args) {
String file = "./samples/remotesensing1";
if (args.length > 0)
file = args[0];
BooleanImage se3 = FlatStructuringElement2D
.createSquareFlatStructuringElement(3);
try {
// Load the image
Image source = (Image) new ImageLoader().process(file + ".png");
Image regions = (Image) new RegionsLoader().process(file);
Image source = (Image) new ImageLoader().process(file + ".png");
Image regions = (Image) new RegionsLoader().process(file);
// Create regions
Image result = (Image) new RegionBuilderWatershedClassical()
.process(source, 0.20);
// View it
new Viewer2D().process(new DrawFrontiersOnImage().process(source,
new FrontiersFromSegmentation().process(result)),
"Segmentation of " + file);
// Create regions
Image result = (Image) new RegionBuilderWatershedClassical()
.process(source, 0.20);
// View it
new Viewer2D().process(new DrawFrontiersOnImage().process(source,
new FrontiersFromSegmentation().process(result)),
"Segmentation of " + file);
System.out.println(new EvalSegmentation().process(result, regions));
} catch (InvalidTypeOfParameterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvalidNumberOfParametersException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}*/
}