1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package org.apache.commons.math3.userguide.genetics;
18 
19 import java.awt.AlphaComposite;
20 import java.awt.Color;
21 import java.awt.Graphics2D;
22 import java.awt.RenderingHints;
23 import java.awt.image.BufferedImage;
24 import java.util.ArrayList;
25 import java.util.List;
26 
27 import org.apache.commons.math3.genetics.AbstractListChromosome;
28 import org.apache.commons.math3.genetics.Chromosome;
29 import org.apache.commons.math3.genetics.InvalidRepresentationException;
30 
31 /**
32  * A simple chromosome representing a list of polygons.
33  */
34 public class PolygonChromosome extends AbstractListChromosome<Polygon> {
35 
36     /** The reference image for fitness testing. */
37     private static BufferedImage refImage;
38     /** The image buffer used to draw the current chromosome during fitness testing. */
39     private static BufferedImage testImage;
40 
setRefImage(BufferedImage ref)41     public static void setRefImage(BufferedImage ref) {
42         refImage = ref;
43     }
44 
setTestImage(BufferedImage image)45     public static void setTestImage(BufferedImage image) {
46         testImage = image;
47     }
48 
PolygonChromosome(List<Polygon> representation)49     public PolygonChromosome(List<Polygon> representation) {
50         super(representation);
51     }
52 
53     @Override
checkValidity(List<Polygon> chromosomeRepresentation)54     protected void checkValidity(List<Polygon> chromosomeRepresentation) throws InvalidRepresentationException {
55         // do nothing
56     }
57 
58     @Override
newFixedLengthChromosome(List<Polygon> chromosomeRepresentation)59     public AbstractListChromosome<Polygon> newFixedLengthChromosome(List<Polygon> chromosomeRepresentation) {
60         return new PolygonChromosome(chromosomeRepresentation);
61     }
62 
63     /**
64      * Return the internal representation, which is needed for our custom mutation policy.
65      *
66      * @return the list of polygons
67      */
getPolygonRepresentation()68     public List<Polygon> getPolygonRepresentation() {
69         return getRepresentation();
70     }
71 
72     /**
73      * Calculate the fitness function for this chromosome.
74      * <p>
75      * For this purpose, we first draw the polygons on the test buffer, and
76      * then compare the resulting image pixel by pixel with the reference image.
77      */
fitness()78     public double fitness() {
79 
80         Graphics2D g2 = testImage.createGraphics();
81 
82         int width = testImage.getWidth();
83         int height = testImage.getHeight();
84 
85         draw(g2, width, height);
86         g2.dispose();
87 
88         int[] refPixels = refImage.getData().getPixels(0, 0, refImage.getWidth(), refImage.getHeight(), (int[]) null);
89         int[] testPixels = testImage.getData().getPixels(0, 0, testImage.getWidth(), testImage.getHeight(), (int[]) null);
90 
91         int diff = 0;
92         int p = width * height * 4 - 1; // 4 channels: rgba
93         int idx = 0;
94 
95         do {
96             if (idx++ % 4 != 0) { // ignore the alpha channel for fitness
97                 int dp = testPixels[p] - refPixels[p];
98                 if (dp < 0) {
99                     diff -= dp;
100                 } else {
101                     diff += dp;
102                 }
103             }
104         } while(--p > 0);
105 
106         return (1.0 - diff / (width * height * 3.0 * 256));
107     }
108 
draw(Graphics2D g, int width, int height)109     public void draw(Graphics2D g, int width, int height) {
110         g.setBackground(Color.WHITE);
111         g.clearRect(0, 0, width, height);
112 
113         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
114         g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
115 
116         List<Polygon> polygons = getPolygonRepresentation();
117         for (Polygon p : polygons) {
118             p.draw(g, width, height);
119         }
120     }
121 
122     @Override
toString()123     public String toString() {
124         return String.format("(f=%s)", getFitness());
125     }
126 
randomChromosome(int polygonLength, int polygonCount)127     public static Chromosome randomChromosome(int polygonLength, int polygonCount) {
128         List<Polygon> list = new ArrayList<Polygon>(polygonCount);
129         for (int j = 0; j < polygonCount; j++) {
130             list.add(Polygon.randomPolygon(polygonLength));
131         }
132         return new PolygonChromosome(list);
133     }
134 
135 }
136