Author: tn
Date: Mon Dec 9 22:34:28 2013
New Revision: 1549696
URL: http://svn.apache.org/r1549696
Log:
Add ImageEvolution example for genetics package.
Added:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/ImageEvolutionExample.java
(with props)
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/Polygon.java
(with props)
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/PolygonChromosome.java
(with props)
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/RandomPolygonMutation.java
(with props)
Added:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/ImageEvolutionExample.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/ImageEvolutionExample.java?rev=1549696&view=auto
==============================================================================
---
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/ImageEvolutionExample.java
(added)
+++
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/ImageEvolutionExample.java
Mon Dec 9 22:34:28 2013
@@ -0,0 +1,205 @@
+package org.apache.commons.math3.userguide.genetics;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+import javax.swing.Box;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+
+import org.apache.commons.math3.genetics.Chromosome;
+import org.apache.commons.math3.genetics.ElitisticListPopulation;
+import org.apache.commons.math3.genetics.GeneticAlgorithm;
+import org.apache.commons.math3.genetics.Population;
+import org.apache.commons.math3.genetics.TournamentSelection;
+import org.apache.commons.math3.genetics.UniformCrossover;
+
+/**
+ * Based on http://www.nihilogic.dk/labs/evolving-images/
+ */
+@SuppressWarnings("serial")
+public class ImageEvolutionExample extends JComponent {
+
+ public static final int POPULATION_SIZE = 40;
+ public static final int TOURNAMENT_ARITY = 2;
+ public static final float MUTATION_RATE = 0.02f;
+ public static final float MUTATION_CHANGE = 0.1f;
+
+ public static final int POLYGON_LENGTH = 6;
+ public static final int POLYGON_COUNT = 100;
+
+ private GeneticAlgorithm ga;
+ private Population currentPopulation;
+ private Chromosome bestFit;
+
+ private Thread internalThread;
+ private volatile boolean noStopRequested;
+
+ private BufferedImage ref;
+ private BufferedImage buf;
+
+ private ImagePainter painter;
+
+ public ImageEvolutionExample() throws Exception {
+ super();
+ setLayout(new FlowLayout());
+
+ Box bar = Box.createHorizontalBox();
+
+ ref = ImageIO.read(new File("resources/canvas_small.png"));
+
+ JLabel picLabel = new JLabel(new ImageIcon(ref));
+ bar.add(picLabel);
+
+ painter = new ImagePainter(ref);
+ bar.add(painter);
+
+ buf = new BufferedImage(ref.getWidth(), ref.getHeight(),
BufferedImage.TYPE_INT_ARGB);
+
+ // TODO: improve this
+ PolygonChromosome.setRefImage(ref);
+ PolygonChromosome.setTestImage(buf);
+
+ add(bar);
+
+ JButton startButton = new JButton("Start");
+ startButton.setActionCommand("start");
+ add(startButton);
+
+ startButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (isAlive()) {
+ stopRequest();
+ } else {
+ startEvolution();
+ }
+ }
+ });
+
+ // initialize a new genetic algorithm
+ ga = new GeneticAlgorithm(new UniformCrossover<Polygon>(0.5), 1.0,
+ new RandomPolygonMutation(MUTATION_RATE,
MUTATION_CHANGE), 1.0,
+ new TournamentSelection(TOURNAMENT_ARITY));
+
+ // initial population
+ currentPopulation = getInitialPopulation();
+ bestFit = currentPopulation.getFittestChromosome();
+ }
+
+ private Population getInitialPopulation() {
+ List<Chromosome> popList = new LinkedList<Chromosome>();
+ for (int i = 0; i < POPULATION_SIZE; i++) {
+ popList.add(PolygonChromosome.randomChromosome(POLYGON_LENGTH,
POLYGON_COUNT));
+ }
+ return new ElitisticListPopulation(popList, popList.size(), 0.25);
+ }
+
+ public boolean isAlive() {
+ return internalThread != null && internalThread.isAlive();
+ }
+
+ public void stopRequest() {
+ noStopRequested = false;
+ internalThread.interrupt();
+ internalThread = null;
+ }
+
+ public void startEvolution() {
+ noStopRequested = true;
+ Runnable r = new Runnable() {
+ public void run() {
+ try {
+ double lastBestFit = Double.MIN_VALUE;
+ int evolution = 0;
+ while (noStopRequested) {
+ currentPopulation =
ga.nextGeneration(currentPopulation);
+
+ System.out.println("generation: " + evolution++ + ": "
+ bestFit.toString());
+ bestFit = currentPopulation.getFittestChromosome();
+
+ if (lastBestFit > bestFit.getFitness()) {
+ System.out.println("gotcha");
+ }
+ lastBestFit = bestFit.getFitness();
+ painter.repaint();
+ }
+ } catch (Exception x) {
+ // in case ANY exception slips through
+ x.printStackTrace();
+ }
+ }
+ };
+
+ internalThread = new Thread(r);
+ internalThread.start();
+ }
+
+ private static void createAndShowGUI() throws Exception {
+ // Create and set up the window.
+ JFrame frame = new JFrame("Image Evolution Example");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ frame.getContentPane().add(new ImageEvolutionExample());
+
+ // Display the window.
+ frame.pack();
+ frame.setSize(new Dimension(500, 300));
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) {
+ // Schedule a job for the event-dispatching thread:
+ // creating and showing this application's GUI.
+ javax.swing.SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ try {
+ createAndShowGUI();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ private class ImagePainter extends Component {
+ BufferedImage ref;
+
+ public ImagePainter(final BufferedImage ref) {
+ this.ref = ref;
+ }
+
+ public Dimension getPreferredSize() {
+ return new Dimension(ref.getWidth(), ref.getHeight());
+ }
+
+ @Override
+ public Dimension getMinimumSize() {
+ return getPreferredSize();
+ }
+
+ @Override
+ public Dimension getMaximumSize() {
+ return getPreferredSize();
+ }
+
+ public void paint(Graphics g) {
+ PolygonChromosome chromosome = (PolygonChromosome) bestFit;
+ chromosome.draw((Graphics2D) g, ref.getWidth(), ref.getHeight());
+ }
+
+ }
+
+}
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/ImageEvolutionExample.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/ImageEvolutionExample.java
------------------------------------------------------------------------------
svn:keywords = Id Revision HeadURL
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/ImageEvolutionExample.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/Polygon.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/Polygon.java?rev=1549696&view=auto
==============================================================================
---
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/Polygon.java
(added)
+++
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/Polygon.java
Mon Dec 9 22:34:28 2013
@@ -0,0 +1,61 @@
+package org.apache.commons.math3.userguide.genetics;
+
+import org.apache.commons.math3.genetics.GeneticAlgorithm;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.util.FastMath;
+
+public class Polygon {
+
+ // the polygon in packed representation:
+ // index | data
+ // 0 | red component
+ // 1 | green component
+ // 2 | blue component
+ // 3 | alpha channel
+ //
+ protected float[] data;
+
+ public static Polygon randomPolygon(int length) {
+ final int polygonSize = 4 + 2 * length;
+
+ final RandomGenerator random = GeneticAlgorithm.getRandomGenerator();
+
+ Polygon p = new Polygon();
+ p.data = new float[polygonSize];
+
+ p.data[0] = random.nextFloat(); // r
+ p.data[1] = random.nextFloat(); // g
+ p.data[2] = random.nextFloat(); // b
+ p.data[3] = FastMath.max(0.2f, random.nextFloat() *
random.nextFloat()); // a
+
+ float px = random.nextFloat();
+ float py = random.nextFloat();
+
+ for (int k = 0; k < length; k++) {
+ p.data[4 + 2*k] = px + (random.nextFloat() - 0.5f);
+ p.data[5 + 2*k] = py + (random.nextFloat() - 0.5f);
+ }
+ return p;
+ }
+
+ public Polygon mutate(float mutationRate, float mutationAmount) {
+ Polygon mutated = new Polygon();
+ int size = data.length;
+ mutated.data = new float[size];
+ for (int i = 0; i < size; i++) {
+ float val = this.data[i];
+ if (GeneticAlgorithm.getRandomGenerator().nextFloat() <
mutationRate) {
+ val += GeneticAlgorithm.getRandomGenerator().nextFloat() *
mutationAmount * 2 - mutationAmount;
+
+ if (val < 0f) {
+ val = 0f;
+ } else if (val > 1f) {
+ val = 1f;
+ }
+ }
+ mutated.data[i] = val;
+ }
+ return mutated;
+ }
+
+}
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/Polygon.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/Polygon.java
------------------------------------------------------------------------------
svn:keywords = Id Revision HeadURL
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/Polygon.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/PolygonChromosome.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/PolygonChromosome.java?rev=1549696&view=auto
==============================================================================
---
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/PolygonChromosome.java
(added)
+++
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/PolygonChromosome.java
Mon Dec 9 22:34:28 2013
@@ -0,0 +1,116 @@
+package org.apache.commons.math3.userguide.genetics;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.GeneralPath;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.genetics.AbstractListChromosome;
+import org.apache.commons.math3.genetics.Chromosome;
+import org.apache.commons.math3.genetics.InvalidRepresentationException;
+
+public class PolygonChromosome extends AbstractListChromosome<Polygon> {
+
+ private static BufferedImage refImage;
+ private static BufferedImage testImage;
+
+ public static void setRefImage(BufferedImage ref) {
+ refImage = ref;
+ }
+
+ public static void setTestImage(BufferedImage image) {
+ testImage = image;
+ }
+
+ public PolygonChromosome(List<Polygon> representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void checkValidity(List<Polygon> chromosomeRepresentation)
throws InvalidRepresentationException {
+ // do nothing
+ }
+
+ @Override
+ public AbstractListChromosome<Polygon>
newFixedLengthChromosome(List<Polygon> chromosomeRepresentation) {
+ return new PolygonChromosome(chromosomeRepresentation);
+ }
+
+ public List<Polygon> getPolygonRepresentation() {
+ return getRepresentation();
+ }
+
+ public double fitness() {
+
+ Graphics2D g2 = testImage.createGraphics();
+
+ int width = testImage.getWidth();
+ int height = testImage.getHeight();
+
+ draw(g2, width, height);
+ g2.dispose();
+
+ int[] refPixels = refImage.getData().getPixels(0, 0,
refImage.getWidth(), refImage.getHeight(), (int[]) null);
+ int[] pixels = testImage.getData().getPixels(0, 0,
testImage.getWidth(), testImage.getHeight(), (int[]) null);
+
+ int diff = 0;
+ int p = width * height * 4 - 1;
+ int idx = 0;
+
+ do {
+ if (idx++ % 4 != 0) {
+ int dp = pixels[p] - refPixels[p];
+ if (dp < 0) {
+ diff -= dp;
+ } else {
+ diff += dp;
+ }
+ }
+ } while(--p > 0);
+
+ return (1.0 - diff / (width * height * 3.0 * 256));
+ }
+
+ public void draw(Graphics2D g, int width, int height) {
+ g.setBackground(Color.RED);
+ g.clearRect(0, 0, width, height);
+
+ //if (true) return;
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
+
+ List<Polygon> polygons = getPolygonRepresentation();
+ for (Polygon p : polygons) {
+ g.setColor(new Color(p.data[0], p.data[1], p.data[2], p.data[3]));
+
+ GeneralPath path = new GeneralPath();
+ path.moveTo(p.data[4] * width, p.data[5] * height);
+
+ int polygonLength = (p.data.length - 4) / 2;
+ for (int j = 1; j < polygonLength; j++) {
+ path.lineTo(p.data[4 + j * 2] * width, p.data[5 + j * 2] *
height);
+ }
+ path.closePath();
+
+ g.fill(path);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("(f=%s)", getFitness());
+ }
+
+ public static Chromosome randomChromosome(int polygonLength, int
polygonCount) {
+ List<Polygon> list = new ArrayList<Polygon>(polygonCount);
+ for (int j = 0; j < polygonCount; j++) {
+ list.add(Polygon.randomPolygon(polygonLength));
+ }
+ return new PolygonChromosome(list);
+ }
+
+}
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/PolygonChromosome.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/PolygonChromosome.java
------------------------------------------------------------------------------
svn:keywords = Id Revision HeadURL
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/PolygonChromosome.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/RandomPolygonMutation.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/RandomPolygonMutation.java?rev=1549696&view=auto
==============================================================================
---
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/RandomPolygonMutation.java
(added)
+++
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/RandomPolygonMutation.java
Mon Dec 9 22:34:28 2013
@@ -0,0 +1,33 @@
+package org.apache.commons.math3.userguide.genetics;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.math3.genetics.Chromosome;
+import org.apache.commons.math3.genetics.MutationPolicy;
+
+public class RandomPolygonMutation implements MutationPolicy {
+
+ private float mutationRate;
+ private float mutationAmount;
+
+ public RandomPolygonMutation(float mutationRate, float mutationAmount) {
+ this.mutationRate = mutationRate;
+ this.mutationAmount = mutationAmount;
+ }
+
+ public Chromosome mutate(Chromosome chromosome) {
+ if (!(chromosome instanceof PolygonChromosome)) {
+ throw new IllegalArgumentException();
+ }
+
+ PolygonChromosome polygons = (PolygonChromosome) chromosome;
+ List<Polygon> repr = polygons.getPolygonRepresentation();
+
+ List<Polygon> newRepr = new ArrayList<Polygon>(repr.size());
+ for (Polygon p : repr) {
+ newRepr.add(p.mutate(mutationRate, mutationAmount));
+ }
+ return new PolygonChromosome(newRepr);
+ }
+}
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/RandomPolygonMutation.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/RandomPolygonMutation.java
------------------------------------------------------------------------------
svn:keywords = Id Revision HeadURL
Propchange:
commons/proper/math/trunk/src/userguide/java/org/apache/commons/math3/userguide/genetics/RandomPolygonMutation.java
------------------------------------------------------------------------------
svn:mime-type = text/plain