Search code examples
javaprocessinggenetic-algorithm

Image reconstruction using a genetic algorithm not evolving


I want to create a genetic algorithm that recreates images. I have created the program for this processing but the images that evolve are not anything close to the input image.

I believe that I have a problem with my fitness function. I have tried many things from changing the polygon types that are part of the DNA, I have tried to do both a crossover and a single parent, and I tried multiple fitness functions: histogram comparison across all channels, pixel comparison, brightness comparison(for black and white images).

    public void calcFitness(PImage tar){
    tar.loadPixels();
    image.loadPixels();
    int brightness = 0;
    for(int i = 0; i < image.pixels.length;i++){
        brightness += Math.abs(parent.brightness(tar.pixels[i])-parent.brightness(image.pixels[i]));
    }
    fitness = 1.0/ (Math.pow(1+brightness,2)/2);
}

public void calculateFitness(){
    int[] rHist= new int[256], gHist= new int[256], bHist = new int[256];
    image.loadPixels();

    //Calculate Red Histogram
    for(int i =0; i<image.pixels.length;i++) {
        int red = image.pixels[i] >> 16 & 0xFF;
        rHist[red]++;
    }

    //Calculate Green Histogram
    for(int i =0; i<image.pixels.length;i++) {
        int green = image.pixels[i] >> 8 & 0xFF;
        gHist[green]++;
    }

    //Calculate Blue Histogram
    for(int i =0; i<image.pixels.length;i++) {
        int blue = image.pixels[i] & 0xFF;
        bHist[blue]++;
    }

    //Compare the target histogram and the current one
    for(int i = 0; i < 256; i++){
        double totalDiff = 0;
        totalDiff += Math.pow(main.rHist[i]-rHist[i],2)/2;
        totalDiff += Math.pow(main.gHist[i]-gHist[i],2)/2;
        totalDiff += Math.pow(main.bHist[i]-bHist[i],2)/2;

        fitness+=Math.pow(1+totalDiff,-1);
    }

}

public void evaluate(){
    int totalFitness = 0;
    for(int i = 0; i<POPULATION_SIZE;i++){
        population[i].calcFitness(target);
        //population[i].calculateFitness();
        totalFitness+=population[i].fitness;
    }
    if(totalFitness>0) {
        for (int i = 0; i < POPULATION_SIZE; i++) {
            population[i].prob = population[i].fitness / totalFitness;
        }
    }
}
   public void selection() {
    SmartImage[] newPopulation = new SmartImage[POPULATION_SIZE];
    for (int i = 0; i < POPULATION_SIZE; i++) {
        DNA child;
        DNA parentA = pickOne();
        DNA parentB = pickOne();
        child = parentA.crossover(parentB);
        child.mutate(mutationRate);
        newPopulation[i] = new SmartImage(parent, child, target.width, target.height);
    }
    population = newPopulation;
    generation++;
}

What I expect from this is to get a general shape and color that is similar to my target image but all I get is random polygons with random colors and alphas.


Solution

  • The code looks fine at first glance. You should first check that your code is capable of converging to a target at all , for example by feeding a target image that is either generated by your algorithm with a random genome (or a very simple image that it should be easily recreated by your algorithm).

    You are using the SAD (sum of absolute differences) metric between pixels to calculate fitness. You can try using SSD (sum of squared differences) like you are doing in the histogram difference method but between pixels or blocks, that will heavily penalize large differences so the remaining images won't be too different from the target. You can try using a more perceptual image space like HSV so the images will be closer visually even if they are farther in RGB space.

    I think comparing the histogram of the entire image may be too lax, as there are many different images that will result in the same histogram. Comparing individual pixels may be too strict, the image needs to be aligned very precisely to get low differences, so everything gets low fitness values unless you are very lucky so the convergence will be too slow. I would recommend that you compare the histogram between overlapping blocks, and don't use all the 256 levels, use only about 16 levels or so (or use some kind of overlapping).

    Read about Histogram of oriented gradients (HOG) and other similar techniques to get ideas to improve your fitness function. I took an online course about object recognition in images, Coursera - Deteccion de Objetos by the University of Barcelona but it's in Spanish. I'm pretty sure you can find similar study materials in English.

    Edit: before trying something more complex a good idea would be doing the SAD or SSD on the average of each overlapping block (which would have a similar effect to strongly blurring the reference and generated images and then comparing the pixels, but faster). The fitness function should be resilient against small changes. An image that it's shifted by a few pixels or that is very similar after discarding the low-level detail should have much better fitness than a very different image and I think blurring will have that effect.