Evolutionary art experiment with Marilyn

You know I like evolutionary algorithms. In the past month I ran a small art experiment reflecting the impact of brands and the harmful effect of advertisements in modern life. The idea was to pick a cultural icon and recreate it using corporate logos only.

Original image

I started with Marilyn Monroe and a genetic algorithm that combined a set of logos in a mutation and a crossover loop. The mutation phase randomly picked some individuals (an individual is a specific combination of logos) and modified them, as nature would do, replacing some logos, resizing or rotating them. The crossover phase chose pairs of individuals and produced a child individual from them.

The first image corresponds to the individual 46, after the algorithm had been running for 15 minutes on a machine with 32 cores. Marylin wasn’t there yet but everything looked good so far.

Individual 46

After 12 hours, Marilyn’s eye and her eyebrow had already been replaced by an Evernote’s elephant and a Facebook logo, as shown in the following image, that belongs to the individual 5449. Her lips seem to be McDonald’s Golden Arches. The algorithm was slower than expected, but it looked promising.

Individual 5449 (12h) Original
Individual 5449 Original image

Sadly, the fittest individual found after 24 hours didn’t look so good, so I cancelled the experiment. I didn’t notice further improvements. The outline of Marilyn is also clear, but the result is way worse than I expected.

Individual 8463 (24h) Original
Individual 5449 Original image

In hindsight, I wouldn’t call the result a success. What do you say?

How to run your own experiment

The good part of genetic algorithms is that you only need to code what you want to achieve and not how to achieve it. That’s why the overall algorithm always look the same. In general you only need to implement the fitness function, that measures how good a given solution is. There are also different crossover algorithms. In this experiment I used a wheel probability selector, where the fittest individuals are more likely to be chosen for crossover and a single point crossover technique.

const population = ... // generate array of random individuals
population
    .filter(individual => individual.fitness > MIN_FITNESS)
    .then(survivors => {
        // always kill the worst 20% individuals
        return survivors
            .sort((a, b) => b.fitness - a.fitness)
            .slice(0, survivors.length * 0.8);
    })
    .then(survivors => {
        // mutate phase
        const mutateCount = survivors.length * MUTATE_PROBABILITY;
        _.times(mutateCount, () => _.sample(survivors).mutate());

        // crossover phase with wheel selection
        const crossoverCount = survivors.length * CROSSOVER_PROBABILITY;
        _.times(crossoverCount, () => {
            const parent1 = wheel();
            const parent2 = wheel();
            const child = parent1.crossover(parent2);
            survivors.push(child);
        });

        return survivors;
    });

Node.js is not the best language to do cpu-intensive tasks, but I wanted to enjoy coding and I found jimp and sharp very handy to compare and process images. Sharp is powered by libvips which makes it really efficient, but it wasn’t enough due to the volume of images I needed to combine. Ideas and cpu-time to make the experiment succeed are welcome! Comments are open.