algorytm.org

Implementacja w JavaScript



Baza Wiedzy
wersja offline serwisu przeznaczona na urządzenia z systemem Android
Darowizny
darowiznaWspomóż rozwój serwisu
Nagłówki RSS
Artykuły
Implementacje
Komentarze
Forum
Bookmarki






Sonda
Implementacji w jakim języku programowania poszukujesz?

Tworzenie obrazów - algorytm genetyczny - Implementacja w JavaScript
Ocena użytkownikóww: *****  / 6
SłabyŚwietny
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;
}
Komentarze
photo
+3 # _marass_ 2012-01-23 17:46
bardzo dobry artykuł :)
Odpowiedz | Odpowiedz z cytatem | Cytować
Dodaj komentarz