Nadesłany przez Tomasz Lubiński, 25 listopada 2011 21:00
Kod przedstawiony poniżej przedstawia główną część rozwiązania problemu.Pobierz pełne rozwiązanie.
Jeżeli nie odpowiada Ci sposób formatowania kodu przez autora skorzystaj z pretty printer'a i dostosuj go automatycznie do siebie.
obrazy.js:
//Generowanie obrazów - algorytm genetyczny
//(c) 2011 by Tomasz Lubinski
//www.algorytm.org
/* canvas data */
var bestCanvas; //canvas with best image
var currentCanvas; //canvas with current image
var width; //width of the image
var height; //height of the image
var pointsNum; //number of point in the image (width*height)
var maxDifference; //maximum difference between images (width*height*sqrt(255*255*3))
var originalImageData; //data that describes source image
var bestSimilarityInHtml; //html element where information about results will be placed
var threadId = -1;
/* genetic algorithm parameters */
var elementsNum = 50; //number of elements
var elementType = 0; //element type (0 - triangle, 1 - circle)
var population = 50; //number of images in population
var elite = 10; //number of images in elite
var mutationProbability = 0.3; //probability of mutation of the element during reproduction
var mutationSize = 10.0; //size of the mutation (0-100)
var geneticImages; //array with images in current generation
var bestSimilarity = 0; //similarity of the best image
var generation = 0; //number of generation
/* class that represents image generated by genetic algorithm */
function geneticImage(elementsNum)
{
this.smilarity = 0; // how this image is similar to original image
this.elements = new Array(elementsNum);// elements that build an image
//initialize with random values
this.init = function()
{
for (var i=0; i<this.elements.length; i++)
{
if (elementType == 0)
{
this.elements[i] = new geneticImageElementTriangle();
}
else
{
this.elements[i] = new geneticImageElementCircle();
}
this.elements[i].init()
}
}
//calculate similarity
this.calculateSimilarity = function(originalImageData, canvas)
{
this.draw(canvas);
var imageData = canvas.getContext("2d").getImageData(0, 0, width, height);
var distance = 0.0;
for (var i=0; i<pointsNum; i++)
{
index = i*4;
distance += Math.sqrt(
(originalImageData.data[index+0]-imageData.data[index+0])*(originalImageData.data[index+0]-imageData.data[index+0])+
(originalImageData.data[index+1]-imageData.data[index+1])*(originalImageData.data[index+1]-imageData.data[index+1])+
(originalImageData.data[index+2]-imageData.data[index+2])*(originalImageData.data[index+2]-imageData.data[index+2]));
}
this.similarity = ((maxDifference - distance) * 100.0) / maxDifference;
}
//draw an image from elements
this.draw = function(canvas)
{
//clear
canvas.width = canvas.width
var ctx = canvas.getContext("2d");
//put white background
ctx.fillStyle = "rgba(255, 255, 255, 1)";
ctx.fillRect(0, 0, width, height);
//draw all elements
for (var i=0; i<this.elements.length; i++)
{
this.elements[i].draw(ctx)
}
}
}
/* class that represents one element of the image generated by genetic algorithm (triangle) */
function geneticImageElementTriangle()
{
//cooridnates
this.x1 = 0;
this.y1 = 0;
this.x2 = 0;
this.y2 = 0;
this.x3 = 0;
this.y3 = 0;
//color
this.r = 0;
this.g = 0;
this.b = 0;
this.a = 0;
//initialize with random values
this.init = function()
{
this.x1 = Math.random()*width;
this.y1 = Math.random()*height;
this.x2 = this.x1 + Math.random()*4.0 - 2.0;
this.y2 = this.y1 + Math.random()*4.0 - 2.0;
this.x3 = this.x1 + Math.random()*4.0 - 2.0;
this.y3 = this.y1 + Math.random()*4.0 - 2.0;
this.r = Math.random()*256.0;
this.g = Math.random()*256.0;
this.b = Math.random()*256.0;
this.a = Math.random();
}
//draw an element on the canvas
this.draw = function(ctx)
{
ctx.beginPath();
ctx.fillStyle = "rgba(" + Math.floor(this.r) + "," + Math.floor(this.g) + "," + Math.floor(this.b) + "," + this.a + ")";
ctx.moveTo(this.x1, this.y1);
ctx.lineTo(this.x2, this.y2);
ctx.lineTo(this.x3, this.y3);
ctx.fill();
ctx.closePath();
}
this.clone = function()
{
var r = new geneticImageElementTriangle();
r.x1 = this.x1;
r.y1 = this.y1;
r.x2 = this.x2;
r.y2 = this.y2;
r.x3 = this.x3;
r.y3 = this.y3;
r.r = this.r;
r.g = this.g;
r.b = this.b;
r.a = this.a;
var mutation = Math.random();
var noChangesProbability = 1.0 - mutationProbability;
var parameterMutationProbability = mutationProbability / 4.0;
if (mutation < noChangesProbability)
{
}
else if (mutation < noChangesProbability + parameterMutationProbability)
{
r.x1 += gaussRandom()*width;
if (r.x1 > width) r.x1 = width;
else if (r.x1 < 0) r.x1 = 0;
r.y1 += gaussRandom()*height;
if (r.y1 > height) r.y1 = height;
else if (r.y1 < 0) r.y1 = 0;
}
else if (mutation < noChangesProbability + 2.0 * parameterMutationProbability)
{
r.x2 += gaussRandom()*width;
if (r.x2 > width) r.x2 = width;
else if (r.x2 < 0) r.x2 = 0;
r.y2 += gaussRandom()*height;
if (r.y2 > height) r.y2 = height;
else if (r.y2 < 0) r.y2 = 0;
}
else if (mutation < noChangesProbability + 3.0 * parameterMutationProbability)
{
r.x3 += gaussRandom()*width;
if (r.x3 > width) r.x3 = width;
else if (r.x3 < 0) r.x3 = 0;
r.y3 += gaussRandom()*height;
if (r.y3 > height) r.y3 = height;
else if (r.y3 < 0) r.y3 = 0;
}
else
{
r.r += gaussRandom()*255;
if (r.r > 255) r.r = 255;
else if (r.r < 0) r.r = 0;
r.g += gaussRandom()*255;
if (r.g > 255) r.g = 255;
else if (r.g < 0) r.g = 0;
r.b += gaussRandom()*255;
if (r.b > 255) r.b = 255;
else if (r.b < 0) r.b = 0;
r.a += gaussRandom();
if (r.a > 1) r.a = 1;
else if (r.a < 0) r.a = 0;
}
return r;
}
}
/* class that represents one element of the image generated by genetic algorithm (circle) */
function geneticImageElementCircle()
{
//cooridnates
this.x1 = 0;
this.y1 = 0;
this.rad = 0;
//color
this.r = 0;
this.g = 0;
this.b = 0;
this.a = 0;
//initialize with random values
this.init = function()
{
this.x1 = Math.random()*width;
this.y1 = Math.random()*height;
this.rad = Math.random()*3.0;
this.r = Math.random()*256.0;
this.g = Math.random()*256.0;
this.b = Math.random()*256.0;
this.a = Math.random();
}
//draw an element on the canvas
this.draw = function(ctx)
{
ctx.fillStyle = "rgba(" + Math.floor(this.r) + "," + Math.floor(this.g) + "," + Math.floor(this.b) + "," + this.a + ")";
ctx.beginPath();
ctx.arc(this.x1, this.y1, this.rad, 0, Math.PI*2, false);
ctx.closePath();
ctx.fill();
}
this.clone = function()
{
var r = new geneticImageElementCircle();
r.x1 = this.x1;
r.y1 = this.y1;
r.rad = this.rad;
r.r = this.r;
r.g = this.g;
r.b = this.b;
r.a = this.a;
var mutation = Math.random();
var noChangesProbability = 1.0 - mutationProbability;
var parameterMutationProbability = mutationProbability / 3.0;
if (mutation < noChangesProbability)
{
}
else if (mutation < noChangesProbability + parameterMutationProbability)
{
r.x1 += gaussRandom()*width;
if (r.x1 > width) r.x1 = width;
else if (r.x1 < 0) r.x1 = 0;
r.y1 += gaussRandom()*height;
if (r.y1 > height) r.y1 = height;
else if (r.y1 < 0) r.y1 = 0;
}
else if (mutation < noChangesProbability + 2.0 * parameterMutationProbability)
{
r.rad += gaussRandom()*((width+height)/4.0);
if (r.rad > ((width+height)/2.0)) r.rad = ((width+height)/2.0);
else if (r.rad < 0) r.rad = 0;
}
else
{
r.r += gaussRandom()*255;
if (r.r > 255) r.r = 255;
else if (r.r < 0) r.r = 0;
r.g += gaussRandom()*255;
if (r.g > 255) r.g = 255;
else if (r.g < 0) r.g = 0;
r.b += gaussRandom()*255;
if (r.b > 255) r.b = 255;
else if (r.b < 0) r.b = 0;
r.a += gaussRandom();
if (r.a > 1) r.a = 1;
else if (r.a < 0) r.a = 0;
}
return r;
}
}
/* random with normal distribution */
function gaussRandom()
{
var x = Math.random();
var y = Math.random();
var r = 0.003 * mutationSize * Math.sqrt(-2.0 * Math.log(x)) * Math.cos(2.0 * Math.PI * y);
if (r<-1.0) r=0;
if (r>1.0) r=0;
return r;
}
/* reproduct new image from "mother" and "father" */
function reproduct(mother, father)
{
r = new geneticImage(elementsNum);
for (var i=0; i<mother.elements.length; i++)
{
if (Math.random() < 0.5)
{
r.elements[i] = mother.elements[i].clone();
}
else
{
r.elements[i] = father.elements[i].clone();
}
}
return r;
}
/* perform one step of genetic algorithm */
function generate()
{
//increment generation counter
generation++;
//calculate similarity
for (var i=0; i<population; i++)
{
geneticImages[i].calculateSimilarity(originalImageData, currentCanvas);
}
//find elite
for (var i=0; i<elite; i++)
{
var currentBest = geneticImages[0].similarity;
var currentBestIndex = 0;
for (var j=1; j<population-i; j++)
{
if (currentBest < geneticImages[j].similarity)
{
currentBest = geneticImages[j].similarity;
currentBestIndex = j;
}
}
//add to elite
eliteImages[i] = geneticImages[currentBestIndex];
geneticImages[currentBestIndex] = geneticImages[population-i-1];
}
//show if new best is found
if (bestSimilarity < eliteImages[0].similarity)
{
bestSimilarity = eliteImages[0].similarity;
eliteImages[0].draw(bestCanvas);
}
bestSimilarityInHtml.innerHTML = "Pokolenie: " + generation + "<br/>Najlepsze dopasowanie: "+bestSimilarity + "%<br/>Aktualne dopasowanie: " + eliteImages[0].similarity + "%";
//create next generation
var i = 0;
var mother = 0;
var father = 0;
while (i<population)
{
if (mother != father)
{
geneticImages[i] = reproduct(eliteImages[mother], eliteImages[father]);
i++;
if (i >= population) break;
}
father++;
if (father >= elite)
{
mother++;
father = 0
}
if (mother >= elite)
{
mother = 0;
father = 1;
}
}
}
/* start generating images */
function stop()
{
clearInterval(threadId);
}
/* start generating images */
function start()
{
//stop, if thread is running
if (threadId != -1)
{
stop();
}
//initialize data
pointsNum = width*height; // number of points in the image
maxDifference = width*height*Math.sqrt(255*255*3); // maximum distance between two images
bestSimilarityInHtml = document.getElementById("bestSimilarity");
bestSimilarity = 0;
generation = 0;
isRunning = true;
//create initial population
geneticImages = new Array(population);
eliteImages = new Array(elite);
for (var i=0; i<population; i++)
{
geneticImages[i] = new geneticImage(elementsNum, width, height);
geneticImages[i].init();
}
//start generation
threadId = setInterval("generate()", 0);
}
/* load image pointed by param_file to canvas */
function loadImage(imgSrc)
{
var canvas = document.getElementById("original");
var ctx = canvas.getContext("2d");
//add file:// if user specified local path
if (imgSrc.indexOf("//") == -1 && imgSrc.indexOf(".") != 0)
{
imgSrc = "file:///" + imgSrc;
}
//load file into canvas
var img = new Image();
img.onload = function(){
bestCanvas = document.getElementById("best");
currentCanvas = document.getElementById("current");
width = img.width;
height = img.height;
canvas.width = width;
canvas.height = height;
bestCanvas.width = width;
bestCanvas.height = height;
currentCanvas.width = width;
currentCanvas.height = height;
ctx.drawImage(img,0,0);
// replace transparent with white
try
{
originalImageData = ctx.getImageData(0, 0, width, height);
} catch(e)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
originalImageData = ctx.getImageData(0, 0, width, height);
}
for (var i=0; i<height; i++)
{
for (var j=0; j<width; j++)
{
index = (i*width+j)*4;
if (originalImageData.data[index+3] == 0)
{
originalImageData.data[index+3] = 255;
originalImageData.data[index+0] = 255;
originalImageData.data[index+1] = 255;
originalImageData.data[index+2] = 255;
}
}
}
}
img.src = imgSrc;
}

