/home/ooechs/ECJ_Tutorial/src/ac/essex/ecj/problems/PixelProblem.java
|
package ac.essex.ecj.problems;
import ec.gp.GPProblem;
import ec.gp.GPIndividual;
import ec.gp.koza.KozaFitness;
import ec.simple.SimpleProblemForm;
import ec.EvolutionState;
import ec.Individual;
import ec.util.Parameter;
import ac.essex.ecj.imaging.PixelLoader;
import ac.essex.ecj.util.TrainingData;
import ac.essex.ecj.util.Counter;
import ac.essex.ecj.data.DoubleData;
import ac.essex.ecj.fitness.FitnessCalculator;
import java.util.Vector;
import java.text.DecimalFormat;
/**
* <p/>
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version,
* provided that any use properly credits the author.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details at http://www.gnu.org
* </p>
*
* @author Olly Oechsle, University of Essex, Date: 17-Aug-2006
* @version 1.0
*/
public class PixelProblem extends GPProblem implements SimpleProblemForm {
public static final String SAVE_RESULTS_TO = "/home/ooechs/ECJ_Tutorial/results/";
public static final String TRAINING_DIRECTORY = "/home/ooechs/ECJ_Tutorial/data/";
/**
* Make the image publicly accessible here. One of the hard things in ECJ is
* passing references around, because you have to override functions with
* fixed signatures. Each node does get passed a reference of this Problem
* object, so all its public members will be accessible to the nodes.
* <p/>
* The PixelLoader is a Java class that allows us to access raw PixelData from
* an image.
*/
public PixelLoader image;
/**
* References to the current location on the image where the GP program is to be run.
*/
public int x, y;
/**
* This Vector will hold all the Training Data.
*/
Vector<TrainingData> trainingData;
/**
* An instance of the data object that passes information between nodes in the tree.
*/
public DoubleData input;
/**
* Remember when we started. Evaluation time is an important consideration in GP.
*/
private long startTime;
public void setup(EvolutionState state, Parameter base) {
// Essential, do not forget this line!
super.setup(state, base);
// set up the input, also essential if you don't like NullPointerExceptions
input = (DoubleData) state.parameters.getInstanceForParameterEq(base.push(P_DATA), null, DoubleData.class);
input.setup(state, base.push(P_DATA));
// Initialise the training data
trainingData = new Vector<TrainingData>(100);
try {
PixelLoader testImage = new PixelLoader(TRAINING_DIRECTORY + "test.bmp");
PixelLoader truthImage = new PixelLoader(TRAINING_DIRECTORY + "truth.bmp");
trainingData.add(new TrainingData(testImage, truthImage));
System.out.println("Loaded " + trainingData.size() + " test images.");
startTime = System.currentTimeMillis();
} catch (Exception e) {
System.out.println("Could not load training data.");
System.exit(1);
}
}
public void evaluate(EvolutionState state, Individual ind, int threadnum) {
/**
* The individual's fitness will be its worst fitness value. This will reward
* generalists.
*/
float overallFitness = -1;
int hits = 0;
/**
* Loop through each training data set
*/
for (int i = 0; i < trainingData.size(); i++) {
TrainingData data = trainingData.elementAt(i);
/**
* Give the GP individuals access to the image by
* putting a reference to it in the publicly available
* "image" member of the problem class.
*/
this.image = data.image;
/**
* These are the statistics that we're intereted in measuring
* We'll pass these to the fitnes calculator which will give us
* a metric as to how good the individual is.
*/
int TP = 0;
int FP = 0;
int totalTruePixels = 0;
/**
* Loop through every pixel on the image, evaluate the program on that
* pixel and then decide if it was right or wrong.
*/
for (int y = 1; y < data.image.getHeight() - 1; y++)
for (int x = 1; x < data.image.getWidth() - 1; x++) {
/**
* Like the image, give the GP inviduals access to
* where we are on the image by putting X and Y into
* the publicly available members on this Problem class
*/
this.x = x;
this.y = y;
/**
* A particularly vile instance of ECJ boilerplate, used to evaluate the individual.
*/
((GPIndividual) ind).trees[0].child.eval(state, threadnum, input, stack, ((GPIndividual) ind), this);
/**
* In a binary classification output, we say that positive values equate to a "yes"
* and negative values to a "no"
*/
boolean result = input.value > 0;
if (data.isExpected(x, y)) {
totalTruePixels++;
if (result) {
TP++;
hits++;
}
} else {
if (result) FP++;
}
}
/**
* Okay, we've evaluated the whole image; it's time to calculate the thisFitness
*/
float thisFitness = FitnessCalculator.getKozaFitness(TP, FP, totalTruePixels);
/**
* Only take the lowest fitness
*/
if (overallFitness == -1 || thisFitness < overallFitness) {
overallFitness = thisFitness;
}
}
overallFitness = FitnessCalculator.createKozaFitness(overallFitness);
KozaFitness f = ((KozaFitness) ind.fitness);
f.setStandardizedFitness(state, overallFitness);
f.hits = hits;
ind.evaluated = true;
// print out a dot, so anybody looking knows ECJ is alive.
System.out.print(".");
}
public void describe(final Individual ind, final EvolutionState state, final int threadnum, final int log, final int verbosity) {
System.out.println("\nBEST INDIVIDUAL'S STATISTICS:");
DecimalFormat formatter = new DecimalFormat("0.00");
// This counts which generation we're on.
Counter.counter++;
try {
/**
* The individual's fitness will be its worst fitness value. This will reward
* generalists.
*/
float overallFitness = -1;
int hits = 0;
/**
* Loop through each training data set
*/
for (int i = 0; i < trainingData.size(); i++) {
TrainingData data = trainingData.elementAt(i);
/**
* Give the GP individuals access to the image by
* putting a reference to it in the publicly available
* "image" member of the problem class.
*/
this.image = data.image;
PixelLoader copy = new PixelLoader(image.getFile());
/**
* These are the statistics that we're intereted in measuring
* We'll pass these to the fitnes calculator which will give us
* a metric as to how good the individual is.
*/
int TP = 0;
int FP = 0;
int TN = 0;
int totalTruePixels = 0;
int totalFalsePixels = 0;
/**
* Loop through every pixel on the image, evaluate the program on that
* pixel and then decide if it was right or wrong.
*/
for (int y = 1; y < data.image.getHeight() - 1; y++)
for (int x = 1; x < data.image.getWidth() - 1; x++) {
/**
* Like the image, give the GP inviduals access to
* where we are on the image by putting X and Y into
* the publicly available members on this Problem class
*/
this.x = x;
this.y = y;
/**
* A particularly vile instance of ECJ boilerplate, used to evaluate the individual.
*/
((GPIndividual) ind).trees[0].child.eval(state, threadnum, input, stack, ((GPIndividual) ind), this);
/**
* In a binary classification output, we say that positive values equate to a "yes"
* and negative values to a "no"
*/
boolean result = input.value > 0;
copy.setPixel(x, y , 0);
if (data.isExpected(x, y)) {
totalTruePixels++;
if (result) {
TP++;
hits++;
copy.setPixelBlue(x, y);
}
} else {
totalFalsePixels++;
if (result) {
FP++;
copy.setPixelRed(x, y);
} else {
TN++;
}
}
}
/** Save the copy **/
copy.save(SAVE_RESULTS_TO + "results_gen" + Counter.getCounterNumber() + "_class " + i + ".bmp");
/**
* Okay, we've evaluated the whole image; it's time to calculate the thisFitness
*/
float thisFitness = FitnessCalculator.getKozaFitness(TP, FP, totalTruePixels);
/**
* Only take the lowest fitness
*/
if (overallFitness == -1 || thisFitness < overallFitness) {
overallFitness = thisFitness;
}
/**
* Work out sensitivity and specificity
*/
double sensitivity = TP / (double) totalTruePixels;
double specificity = TN / (double) totalFalsePixels;
System.out.println("TP (Sensitivity) : " + sensitivity);
System.out.println("TN (Specificity) : " + specificity);
}
overallFitness = FitnessCalculator.createKozaFitness(overallFitness);
System.out.println("Overall Fitness: " + formatter.format(overallFitness) + ", hits: " + hits);
System.out.println("Saved results as: results_gen" + Counter.getCounterNumber() + "_class[n].bmp");
System.out.println("Total Time So Far: " + ((System.currentTimeMillis() - startTime) / 1000) + " seconds");
System.out.println();
} catch (Exception e) {
e.printStackTrace();
}
}
}