/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(); 
        } 
    } 
 
}