genetic algorithm - make image with transparent polygon
1. mutate parent(add random polygon or delete, change color, alpha) - make child
2. compare with original image
3. if child's fitness is better than parent, child is parent.
4. go to 1
Reference - http://linesandcolors.com/2008/12/10/genetic-programming-evolution-of-mona-lisa/
p.s. : it takes a lot of time to see a good image..
/**
* Copyright greentec ( http://wonderfl.net/user/greentec )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/gA9S
*/
package {
import com.bit101.components.Label;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent; import flash.events.TimerEvent;
import flash.geom.Point;
import flash.net.URLRequest;
import com.bit101.components.PushButton;
import flash.utils.ByteArray;
import flash.utils.getTimer;
import flash.geom.ColorTransform;
import flash.system.LoaderContext;
import flash.geom.Matrix;
public class FlashTest extends Sprite {
public var imageLoad:URLRequest = new URLRequest("http://assets.wonderfl.net/images/related_images/a/a8/a8bf/a8bf5cfb82b059900c5b8432115e55c78448f816m");
public var imageLoad_Sprite:Sprite;
public var imageLoad_Loader:Loader;
public var imageBitmapData:BitmapData;
public var imageBitmap:Bitmap;
public var imageWidth:int;
public var imageHeight:int;
public var defaultAlpha:Number = 0.1;
public var defaultAlphaMax:Number = 0.6;
public var defaultAlphaMin:Number = 0.1;
public var testBitmapData:BitmapData;
public var testBitmap:Bitmap;
public var viewBitmapData:BitmapData;
public var viewBitmap:Bitmap;
public var genBitmapData:BitmapData;
public var genBitmap:Bitmap;
public var genSprite:Sprite;
public var showingTestImage:Boolean = true;
public var costArray:Array;
public var genCostArray:Array;
//public var genCostSortArray:Array;
public var genSortArray:Array;
public var populationArray:Array;
public var childrenArray:Array;
public var populationTriangleSize:uint = 10;//150;//1000;//1000개의 원 //40; //20개의 triangle
public var circleMaxRadius:uint = 50;
public var populationNum:uint = 2//10; //30개의 init poputation
//public var iterationNum:uint = 50;
public var popCost:uint;
public var childCost:uint;
public var IsDirty:Boolean = false;
public var PlaneMax:int = 150;
public var PlaneMin:int = 4;
public var PointPerPlaneMin:int = 3;
public var PointPerPlaneMax:int = 10;
public var NewPlaneRate:Number = 0.2;
public var NewPointRate:Number = 0.2;
public var CloneRate:Number = 0.3;
//public var NewColorRate:Number = 0.2;
public var DelPlaneRate:Number = 0.2;
public var DelPointRate:Number = 0.2;
public var MovePointRate:Number = 0.2;
public var NewRedRate:Number = 0.25;
public var NewGreenRate:Number = 0.25;
public var NewBlueRate:Number = 0.25;
public var NewAlphaRate:Number = 0.25;
public var scaleValue:int = 3;
public var b_circle:Boolean = false;//true//false;//true;
public var crossoverPointNum:uint = 1;
public var mutationRate:Number = 0.2;
public var eliteRate:Number = 0.5; //50%는 그대로 남는다.
public var generation:uint = 0;
//public var eliminationList:Array;
//public var parentList:Array;
public var getNewGenButton:PushButton;
public var genLabel:Label;
public var diffLabel:Label;
public var matchLabel:Label;
public var planeLabel:Label;
public var validLabel:Label;
public var validgen:uint = 0;
public var timecheck:uint;
public var loopFinished:Boolean = false;
public var imageLoaded:Boolean = false;
public var totalErr:uint;
public function FlashTest() {
// write as3 code here..
Wonderfl.disable_capture();
stage.frameRate = 60;
stage.scaleMode = "noScale";
imageLoad_Loader = new Loader();
imageLoad_Sprite = new Sprite();
genCostArray = [];
imageLoad_Loader.contentLoaderInfo.addEventListener(Event.COMPLETE, testImageLoad);
imageLoad_Loader.load(imageLoad, new LoaderContext(true));
}
private function testImageLoad(e:Event):void
{
//imageWidth = e.target.width;
//imageHeight = e.target.height;
imageWidth=88;
imageHeight=103;
genSprite = new Sprite();
//testBitmapData = new BitmapData(int(imageWidth * scaleValue), int(imageHeight * scaleValue), true, 0xff000000);
//testBitmap = new Bitmap(testBitmapData);
//genSprite.addChild(testBitmap);
testBitmapData = new BitmapData(imageWidth, imageHeight, true, 0xff000000);
testBitmap = new Bitmap(testBitmapData);
viewBitmapData = new BitmapData(int(imageWidth * scaleValue), int(imageHeight * scaleValue), true, 0xff000000);
viewBitmap = new Bitmap(viewBitmapData);
genSprite.addChild(testBitmap);
genSprite.addChild(viewBitmap);
addChild(genSprite);
imageBitmapData = new BitmapData(imageWidth, imageHeight, true);
imageBitmapData.draw(e.target.content, new Matrix(103 / e.target.width, 0, 0, 103 / e.target.width, -10));
imageBitmap = new Bitmap(imageBitmapData);
imageLoad_Sprite = new Sprite();
imageLoad_Sprite.addChild(imageBitmap);
if (showingTestImage == true)
{
addChild(imageLoad_Sprite);
}
init_cost(imageBitmapData, imageWidth, imageHeight);
//if(use_imagePalette==true)
//{
//imagePalette = [];
//imagePalette = ColourUtils.colourPalette( imageBitmapData, 128, 0.005 );
//trace(imagePalette.length);
//}
imageLoaded = true;
var i:int, j:int;
totalErr = 0;
for (i = 0; i < costArray.length; i += 1)
{
for (j = 0; j < costArray[i].length; j += 1)
{
totalErr += costArray[i][j];
}
}
genSprite.x = imageWidth;
testBitmap.x = -imageWidth;
testBitmap.y = imageHeight;
//genSprite.scaleX = 3;
//genSprite.scaleY = 3;
init_population();
timecheck = getTimer();
//trace(timecheck);
//loop_GA();
get_popoulationimageBitmapData(populationArray, 1, false);
popCost=calculate_cost();
stage.addEventListener(Event.ENTER_FRAME, loop_GA);
}
private function init_cost(imageBitmapData:BitmapData, width:int, height:int ):void //불러들인 이미지의 color 값을 읽어들여 costArray에 저장한다
{
var i:int;
var j:int;
costArray = [];
//genBitmapData = new BitmapData(width, height, true, 0x000000);//0xff0f1012);
//genBitmapData.lock();
for (i = 0; i < width; i+=1)
{
costArray[i] = new Array();
for (j = 0; j < height; j+=1)
{
var color:uint=imageBitmapData.getPixel32(i, j)
//costArray[i].push((color & 0xff0000) >> 16); //red
//costArray[i].push((color & 0x00ff00) >> 8); //green
//costArray[i].push(color & 0x0000ff); //blue
costArray[i].push((color >> 16) & 0xff);
costArray[i].push((color >> 8) & 0xff);
costArray[i].push(color & 0xff);
//costArray[i].push(imageBitmapData.getPixel32(i, j));
//genBitmapData.setPixel32(i,j,costArray[i][(costArray.length - 1)]);
}
}
//genBitmapData.unlock();
var colorTra:ColorTransform = new ColorTransform(0, 0, 0, 1, 0, 0, 255);
genLabel = new Label(this, 5, 24+300, "Generation : " + generation);
diffLabel = new Label(this, 5, 36+300, "Population Difference");
matchLabel = new Label(this, 5, 0+300, "Match : ");
planeLabel = new Label(this, 5, 72+300, "Polygon : ");
validLabel = new Label(this, 5, 84+300, "Valid : ");
genLabel.transform.colorTransform = colorTra;
diffLabel.transform.colorTransform = colorTra;
planeLabel.transform.colorTransform = new ColorTransform(0, 0, 0, 1, 255, 128, 0);
validLabel.transform.colorTransform = new ColorTransform(0, 0, 0, 1, 0, 128, 255);
}
private function init_population():void
{
var i:int;
populationArray = new Array();
for (i = 0; i < PlaneMin; i+=1)
{
//var plane:Plane = new Plane(imageWidth, imageHeight);
populationArray.push(new Plane(imageWidth, imageHeight));
//trace(populationArray[i].points_);
}
//trace(populationArray.length);
}
private function loop_GA(e:Event):void//(t:TimerEvent):void//(e:MouseEvent):void//(t:TimerEvent)
{
for (var j:int = 0; j < 10; j+=1)
{
if (imageLoaded == true)
{
generation += 1;
var i:int=0;
//genCostSortArray = [];
childrenArray = [];
childrenArray = clone(populationArray);
mutate();
//get_popoulationimageBitmapData(populationArray, 1, false);
//popCost=calculate_cost();
get_popoulationimageBitmapData(childrenArray, 1, false);
childCost=calculate_cost();
//trace(popCost, childCost);
if (childCost < popCost)
{
popCost=childCost;
drawElite();
populationArray = null;
populationArray = clone(childrenArray);
childrenArray = null;
//populationIsDrawn = true;
validgen += 1;
printdiffLabel(childCost);
printmatchLabel(childCost);
printplaneLabel();
}
else
{
printdiffLabel(popCost);
printmatchLabel(popCost);
}
printgenerationLabel();
printvalidLabel();
//timetick = new Timer(50);
//timetick.start();
//timetick.addEventListener(TimerEvent.TIMER_COMPLETE, loop_Rest);
//loopFinished = true;
//timetick.start();
//timetick.addEventListener(TimerEvent.TIMER, loop_GA2);
//test
//crossover();
//mutate();
//getNewGenButton.addEventListener(MouseEvent.CLICK, loop_GA);
//addChild(getNewGenButton);
//trace(parentList.length, eliminationList.length, genCostSortArray.length);
}
}
}
private function get_popoulationimageBitmapData(list:Array, scaleValue:Number, visible:Boolean):void
{
var triangle:Sprite = new Sprite;
var g:Graphics = triangle.graphics;
//testBitmapData = new BitmapData(imageWidth * scaleValue, imageHeight * scaleValue, true, 0xff000000);// , 0xff0f1012);
if (visible == false)
{
testBitmapData.fillRect(testBitmapData.rect, 0xff000000);
testBitmapData.lock();
}
else
{
viewBitmapData.fillRect(viewBitmapData.rect, 0xff000000);
viewBitmapData.lock();
}
var j:int;
var k:int;
var color:uint;
testBitmapData.lock();
g.lineStyle(0, 0, 0);
g.clear();
for (j = 0; j < list.length; j+=1)
{
var plane:Object = list[j];
color = (plane.red << 16) | (plane.green << 8) | plane.blue;
g.beginFill(color, plane.alpha_);
//trace(plane.alpha_)
g.moveTo(plane.points_[0].x * scaleValue, plane.points_[0].y * scaleValue);
for (k = 1; k < plane.points_.length; k += 1)
{
g.lineTo(plane.points_[k].x * scaleValue, plane.points_[k].y * scaleValue);
}
g.lineTo(plane.points_[0].x * scaleValue, plane.points_[0].y * scaleValue);
g.endFill();
//trace(list.length);
//trace(plane.points_);
}
if (visible == false)
{
testBitmapData.draw(triangle);
testBitmapData.unlock();
}
else
{
viewBitmapData.draw(triangle);
viewBitmapData.unlock();
}
//testBitmap = new Bitmap(testBitmapData);
//genSprite.addChild(testBitmap);
}
private function calculate_cost():uint
{
//genCostSortArray = [];
var diff:uint;
var i:int;
var j:int;
var r:int, g:int, b:int;
testBitmapData.lock();
for (i = 0; i < imageWidth; i+=1)
{
for (j = 0; j < imageHeight; j+=1)
{
var testcolor:uint = testBitmapData.getPixel32(i, j);
r = costArray[i][3 * j] - ((testcolor >> 16) & 0xff);
r = (r ^ (r >> 31)) - (r >> 31);
g = costArray[i][3 * j + 1] - ((testcolor >> 8) & 0xff);
g = (g ^ (g >> 31)) - (g >> 31);
b = costArray[i][3 * j + 2] - (testcolor & 0xff);
b = (b ^ (b >> 31)) - (b >> 31);
diff += r + g + b;
}
}
testBitmapData.unlock();
return diff;
//genCostSortArray.push(diff);
//trace(diff);
//genCostSortArray.push( { cost:diff, index:populationIndex } );
//trace(genCostSortArray);
}
private function mutate():void
{
//Delete Plane or Point
var randPlane:int;
IsDirty = false;
if (IsDirty == false && Math.random() < DelPlaneRate)
{
if (childrenArray.length > PlaneMin)
{
randPlane=int(Math.random()*childrenArray.length)
childrenArray.splice(randPlane, 1);
IsDirty = true;
}
}
if (IsDirty == false && Math.random() < DelPointRate)
{
if (childrenArray[randPlane].points_.length > PointPerPlaneMin)
{
randPlane = int(Math.random()*childrenArray.length)
childrenArray[randPlane].points_.splice(int(childrenArray[randPlane].points_.length * Math.random()), 1);
IsDirty = true;
}
}
if (IsDirty == false && Math.random() < NewPlaneRate)
{
if (childrenArray.length < PlaneMax)
{
var plane:Object;
if (Math.random() < CloneRate)
{
randPlane = int(Math.random() * childrenArray.length);
plane = clone(childrenArray[randPlane]);
if (Math.random() < NewRedRate)
{
plane.red = Math.random() * 255;
}
if (Math.random() < NewGreenRate)
{
plane.green = Math.random() * 255;
}
if (Math.random() < NewBlueRate)
{
plane.blue = Math.random() * 255;
}
if (Math.random() < NewAlphaRate)
{
plane.alpha_ = Math.random() * 0.1;
}
}
else
{
plane = new Plane(imageWidth, imageHeight);
//childrenArray[childrenArray.length - 1].red = childrenArray[randPlane].red + Math.random() * 20 - 10;
//childrenArray[childrenArray.length - 1].green = childrenArray[randPlane].green + Math.random() * 20 - 10;
//childrenArray[childrenArray.length - 1].blue = childrenArray[randPlane].blue + Math.random() * 20 - 10;
}
childrenArray.push(plane);
IsDirty = true;
}
}
if (IsDirty == false && Math.random() < MovePointRate)
{
randPlane = int(Math.random()*childrenArray.length)
childrenArray[randPlane].points_[int(childrenArray[randPlane].points_.length * Math.random())] = new Point(Math.random()*imageWidth,Math.random()*imageHeight);
IsDirty = true;
}
//if (Math.random() < NewColorRate)
//{
//childrenArray[randPlane].setRandomColor();
//}
if (IsDirty == false && Math.random() < NewPointRate)
{
if (childrenArray[randPlane].points_.length < PointPerPlaneMax)
{
randPlane = int(Math.random()*childrenArray.length)
childrenArray[randPlane].points_.push(new Point(Math.random() * imageWidth, Math.random() * imageHeight));
IsDirty = true;
}
}
if (IsDirty == false && Math.random() < NewRedRate)
{
randPlane = int(Math.random() * childrenArray.length)
childrenArray[randPlane].red = Math.random() * 255;
IsDirty = true;
}
if (IsDirty == false && Math.random() < NewGreenRate)
{
randPlane = int(Math.random() * childrenArray.length)
childrenArray[randPlane].green = Math.random() * 255;
IsDirty = true;
}
if (IsDirty == false && Math.random() < NewBlueRate)
{
randPlane = int(Math.random() * childrenArray.length)
childrenArray[randPlane].blue = Math.random() * 255;
IsDirty = true;
}
if (IsDirty == false && Math.random() < NewAlphaRate)
{
randPlane = int(Math.random() * childrenArray.length)
childrenArray[randPlane].alpha_ = Math.random() * 0.1;
IsDirty = true;
}
}
private function drawElite():void
{
//genSprite.removeChild(testBitmap);
get_popoulationimageBitmapData(childrenArray, scaleValue, true);
//get_popoulationimageBitmapData(genSortArray[0]);
//trace("index : ", genCostSortArray[0].index, "cost : ", genCostSortArray[0].cost);
//testBitmap = new Bitmap(testBitmapData);
//genSprite.addChild(testBitmap);
}
private function printdiffLabel(diffScore:uint):void
{
var i:int;
var str:String="";
str += "Population Difference";
str += "\n " + diffScore;
diffLabel.text = str;
}
private function printgenerationLabel():void
{
genLabel.text = "Generation : " + generation + " (" + String(int(generation/(getTimer()-timecheck)*100000)/100) +" gen/s)";
}
private function printmatchLabel(diffScore:uint):void
{
matchLabel.text = "Match : " + String(int((totalErr-diffScore)/totalErr * 100000)/1000)+" %";
}
private function printplaneLabel():void
{
planeLabel.text = "Polygon : " + String(populationArray.length);
}
private function printvalidLabel():void
{
validLabel.text = "Valid : " + String(int(validgen / generation * 10000) / 100) +"%";
}
static public function clone(source:Object):*{
var byteArray:ByteArray = new ByteArray();
byteArray.writeObject(source);
byteArray.position = 0;
return(byteArray.readObject());
}
}
}
Class
{
import flash.geom.Point;
import flash.display.Sprite;
/**
* ...
* @author ypc
*/
class Plane extends Sprite
{
public var points_:Array=[];
public var alpha_:Number;
public var red:int;
public var green:int;
public var blue:int;
public function Plane(width:Number,height:Number)
{
setRandomColor();
for (var j:int = 0; j < 3; j++)
{
this.points_.push(new Point(Math.random() * width, Math.random() * height));
}
}
public function setRandomColor():void
{
//this.alpha_ = Math.random()*0.5+0.1;
this.alpha_ = 0.1;
this.red = Math.random() * 255;
this.green = Math.random() * 255;
this.blue = Math.random() * 255;
}
//public function addPoint(width:Number,height:Number):void
//{
//this.points_.push(new Point(Math.random() * width, Math.random() * height));
//
//}
}
}