★うさぎの画像を遺伝的アルゴリズムで作ってみた
モナリザの画像を遺伝的アルゴリズムで解析し、多角形の色や透明度、大きさを重ね合わせて入力画像に近づけていくという仕組みです。
ただしソースがJavascriptで作られており、ブラウザーの種類やVersionによって動いたり動かなかったりで開発には不向きなので、Processingで同じアルゴリズムを再現して見ました。
中身を解読してわかったのは、突然変異のみを使った、簡易的な遺伝的アルゴリズムですね。多角形の大きさとか色をランダムに作って重ねていくので、ある程度処理が進むとそこからさきは膨大な時間がかかります。
何に使えるかはよくわかりませんが、ネタとして興味のある人は触ってみてください。
<注意点>
・入力画像は200x200の解像度で、sketchフォルダ直下に置きます。・画像の名前はrabit.jpgとなってますので、画像の名前を合わせるか、sketchの該当箇所のjpeg名を変更してください。
以下、ソースです↓
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
PImage input_image; | |
String IMG_INIT ="rabit.jpg"; //入力画像のファイル名 | |
int IWIDTH = 0;//画像のピクセル幅 | |
int IHEIGHT = 0;//画像のピクセル高さ | |
int COUNTER_TOTAL = 0;//計算の回数 | |
int MAX_SHAPES = 50;//一度に計算する多角形の個数 | |
int MAX_POINTS = 6;//多角形の点の数 | |
int CHANGED_SHAPE_INDEX = 0;//突然変異のとき、50個の多角形のうち一つを指定する番号 | |
int CHANGED_POINT_INDEX = 0;//突然変異のとき、6個の点のうち一つを指定する番号 | |
int count = 1;//スクリーンショットの連番用変数 | |
boolean flag = true;//ENTERキーのON/OFF判定スイッチ | |
int ms = 0;//経過時間 | |
int hr;//時間差分 | |
int sum1,sum2;//停止時間、再開時間の合計値 | |
/*配列*/ | |
Integer[][] dna_test_point = new Integer[12][MAX_SHAPES]; | |
Integer[][] dna_best_point = new Integer[12][MAX_SHAPES]; | |
Integer[][] dna_test_color = new Integer[4][MAX_SHAPES]; | |
Integer[][] dna_best_color = new Integer[4][MAX_SHAPES]; | |
color[] data_input;//入力画面のピクセル情報を格納する配列 | |
color[] data_test;//TEST画面のピクセル情報を格納する配列 | |
ArrayList<Integer> stop_time = new ArrayList<Integer>(); | |
ArrayList<Integer> start_time = new ArrayList<Integer>(); | |
/*評価関数*/ | |
Float FITNESS_MAX = 999923404.0;//初期値 | |
Float FITNESS_TEST = FITNESS_MAX;//TEST画面の評価関数 | |
Float FITNESS_BEST = FITNESS_MAX;//BEST画面の評価関数 | |
Float FITNESS_BEST_NORMALIZED = 0.0; // pixel match: 0% worst - 100% best | |
int NORM_COEF = 0; // maximum distance between black and white images | |
/*フォント*/ | |
PFont font; | |
String[] fontList = PFont.list();//パソコン内部のフォントリストを作成 | |
void setup(){ | |
size(800,300);//ウィンドウサイズ | |
background(0);//背景を黒色 | |
smooth(); | |
frameRate(60); | |
/*文字の表示*/ | |
font = createFont("Arial",15); //フォントリストの中から選ぶ この際に最大サイズを決める | |
textFont(font); //設定したフォントを使用 | |
textSize(20); //サイズを最終決定 | |
textAlign(CENTER); | |
text("Input_Image",150,270); | |
text("Update_Image",400,270); | |
text("Test_Image",650,270); | |
/*入力画像の表示*/ | |
input_image = loadImage(IMG_INIT);//入力画像の読み込み | |
image(input_image, 50, 50); //画像の左上を(50,100)に指定して表示 | |
IWIDTH = input_image.width;//入力画像の幅を取得 | |
IHEIGHT = input_image.height;//入力画像の高さを取得 | |
//SUBPIXELS = IWIDTH*IHEIGHT*DEPTH; | |
NORM_COEF = IWIDTH*IHEIGHT*3*255; | |
/*入力画像のピクセル情報の取得*/ | |
input_image.loadPixels();//入力画像のピクセル情報を取得 | |
data_input = new color[IWIDTH*IHEIGHT];//色情報を格納するカラー型配列を作成 | |
for (int i = 0; i < IWIDTH*IHEIGHT; i++) {//入力画像のカラー情報を格納(RGBA) | |
data_input[i] = input_image.pixels[i]; | |
} | |
/*CANVASの作成*/ | |
stroke(125); | |
strokeWeight(2); | |
fill(0,0,0); | |
rect(300,50,IWIDTH,IHEIGHT);//BEST画面 | |
rect(550,50,IWIDTH,IHEIGHT);//TEST画面 | |
/*初期のDNAを作成*/ | |
init_dna(dna_test_point,dna_test_color); | |
init_dna(dna_best_point,dna_best_color); | |
/*TESTからBESTにDNAをコピーして同じ状態にする*/ | |
copyDNA_point(dna_test_point,dna_best_point); | |
copyDNA_color(dna_test_color,dna_best_color); | |
/*TEST画面とBEST画面に初期DNAを描画*/ | |
drawDNA_test(dna_test_point,dna_test_color); | |
drawDNA_best(dna_best_point,dna_best_color); | |
} | |
/*キーを押したときの処理*/ | |
void keyPressed(){ | |
if(key == ENTER){//ENTERキーを押したら処理を中断 | |
if(flag == true){ | |
stop_time.add(millis());//停止時間をアレイリストに格納 | |
noLoop();//drawを停止 | |
flag = false; | |
} | |
else if(flag == false){//ENTERキーをもう一度押したら処理を再開 | |
start_time.add(millis());//再開時間をアレイリストに格納 | |
count_time();//時間差分を計算 | |
flag = true; | |
loop();//再開 | |
} | |
} | |
if(key == 'p'){//スペースキーを押したら画面を保存 | |
String path = count + ".jpg"; | |
save(path); | |
count++; | |
} | |
} | |
void count_time(){ | |
sum1 = 0;//初期化 | |
sum2 = 0;//初期化 | |
for(int i = 0 ; i < stop_time.size();i++){//停止時間の和 | |
sum1 += (stop_time.get(i)); | |
} | |
for(int j = 0 ; j < start_time.size();j++){//再開時間の和 | |
sum2 += (start_time.get(j)); | |
} | |
hr =sum2 - sum1;//差分をとる | |
} | |
void draw(){ | |
ms = millis() - hr;//経過時間を取得 | |
int s = (ms/1000)%60;//秒 | |
int m = floor(ms/60000)%60;//分 | |
int h = floor(ms/(60000*60))%60;//時 | |
/*テキストの表示*/ | |
textAlign(CORNER); | |
fill(0,0,0);//黒 | |
rect(0,0,700,49);//表示をいったん消す | |
fill(255,255,255);//文字の色 | |
text("Fitness : " + FITNESS_BEST_NORMALIZED + "%" , 5, 20);//文字 | |
text("Count : " + COUNTER_TOTAL , 170, 20);//文字 | |
text("Elapsed_Time : " + h + "h " + m + "m " + s + "s " , 400, 20);//文字 | |
evolve(); | |
} | |
/*初期DNAの作成*/ | |
void init_dna(Integer[][] pt,Integer[][] col){ | |
for (int x = 0; x < MAX_SHAPES; x++){//0から49まで | |
for (int y = 0; y < MAX_POINTS; y++){ | |
pt[y][x] = round(random(1)*IWIDTH);//6個のランダムな点のX座標をpoints配列に格納 | |
} | |
for (int y = MAX_POINTS; y < MAX_POINTS*2; y++){ | |
pt[y][x] = round(random(1)*IHEIGHT);//6個のランダムな点のY座標をpoints配列に格納 | |
} | |
color c = color(round(255*(random(1))),round(255*(random(1))),round(255*(random(1))),round(0.001*255));//一番最初の50個のDNAの色 | |
//color c = color(255,255,255,round(0.001*255));//黒 | |
col[0][x] = int(red(c)); | |
col[1][x] = int(green(c)); | |
col[2][x] = int(blue(c)); | |
col[3][x] = int(alpha(c)); | |
} | |
} | |
/*DNAの点をコピー*/ | |
void copyDNA_point(Integer[][] dna_from, Integer[][] dna_to){ | |
for (int x = 0; x < MAX_SHAPES; x++){ | |
for (int y = 0; y < MAX_POINTS*2; y++){//6個のランダムな点の座標をpoints配列に格納 | |
dna_to[y][x] = dna_from[y][x]; | |
} | |
} | |
} | |
/*DNAの色をコピー*/ | |
void copyDNA_color(Integer[][] dna_from,Integer[][] dna_to){ | |
for(int x=0;x<MAX_SHAPES;x++){//0から49まで | |
dna_to[0][x] = dna_from[0][x]; | |
dna_to[1][x] = dna_from[1][x]; | |
dna_to[2][x] = dna_from[2][x]; | |
dna_to[3][x] = dna_from[3][x]; | |
} | |
} | |
/*TEST画面を描画*/ | |
void drawDNA_test(Integer[][] pt,Integer[][] col){ | |
noStroke();//線なし | |
for(int x=0;x<MAX_SHAPES;x++) {//0から49まで | |
fill(col[0][x],col[1][x],col[2][x],col[3][x]); | |
pushMatrix(); | |
translate(550,50); | |
beginShape(); | |
for(int y = 0;y<MAX_POINTS;y++){ | |
vertex(pt[y][x],pt[y+MAX_POINTS][x]); | |
} | |
endShape(CLOSE); | |
popMatrix(); | |
} | |
} | |
/*BEST画面を描画*/ | |
void drawDNA_best(Integer[][] pt,Integer[][] col){ | |
noStroke();//線なし | |
for(int x=0;x<MAX_SHAPES;x++) {//0から49まで | |
fill(col[0][x],col[1][x],col[2][x],col[3][x]); | |
pushMatrix(); | |
translate(300,50); | |
beginShape(); | |
for(int y = 0;y<MAX_POINTS;y++){ | |
vertex(pt[y][x],pt[y+MAX_POINTS][x]); | |
} | |
endShape(CLOSE); | |
popMatrix(); | |
} | |
} | |
/**/ | |
void evolve(){ | |
mutateDNA(dna_test_point,dna_test_color);//初期DNA(50個の多角形)に対し、変更(突然変異)を加えていく | |
drawDNA_test(dna_test_point,dna_test_color);//TEST画面の描画 | |
compute_fitness();//評価関数の計算 | |
if(FITNESS_TEST < FITNESS_BEST){//FITNESSがより小さくなったら | |
pass_gene_mutation_pt(dna_test_point,dna_best_point,CHANGED_SHAPE_INDEX);//TEST画面からBEST画面に50個の点の座標データをコピー | |
pass_gene_mutation_color(dna_test_color,dna_best_color,CHANGED_SHAPE_INDEX);//TEST画面からBEST画面に色データをコピー | |
FITNESS_BEST = FITNESS_TEST;//FITBESS_BESTを更新 | |
drawDNA_best(dna_best_point,dna_best_color);//BEST画面の描画 | |
FITNESS_BEST_NORMALIZED = round(1000*(1.0-(FITNESS_BEST/ NORM_COEF)))/10.0; //評価関数の割合を計算 小数点一桁にするため、10倍の四捨五入してから10で除算 | |
} | |
else{//FITNESSが小さくならないときは | |
pass_gene_mutation_pt(dna_best_point,dna_test_point,CHANGED_SHAPE_INDEX);//BEST画面からTEST画面にコピー | |
pass_gene_mutation_color(dna_best_color,dna_test_color,CHANGED_SHAPE_INDEX);//BEST画面からTEST画面にコピー | |
} | |
COUNTER_TOTAL++; | |
} | |
/*点の座標データをコピー*/ | |
void pass_gene_mutation_pt(Integer[][] dna_from,Integer[][] dna_to,int gene_index){ | |
for (int y = 0; y < MAX_POINTS*2; y++){//6個のランダムな点の座標をpoints配列に格納 | |
dna_to[y][gene_index] = dna_from[y][gene_index]; | |
} | |
} | |
/*色データをコピー*/ | |
void pass_gene_mutation_color(Integer[][] dna_from,Integer[][] dna_to,int gene_index){ | |
dna_to[0][gene_index] = dna_from[0][gene_index];//red | |
dna_to[1][gene_index] = dna_from[1][gene_index];//green | |
dna_to[2][gene_index] = dna_from[2][gene_index];//blue | |
dna_to[3][gene_index] = dna_from[3][gene_index];//alpha | |
} | |
/*突然変異*/ | |
void mutateDNA(Integer[][] pt, Integer[][] col){ | |
CHANGED_SHAPE_INDEX = round((MAX_SHAPES)*random(1))-1; | |
if(CHANGED_SHAPE_INDEX < 0){CHANGED_SHAPE_INDEX = 0;} | |
Float rourette = random(2);//突然変異のときに用いるサイコロを振る | |
if(rourette<1){ | |
if(rourette<0.25){ | |
col[0][CHANGED_SHAPE_INDEX] = round(255*random(1));//redを置き換え | |
} | |
else if(rourette < 0.5){ | |
col[1][CHANGED_SHAPE_INDEX] = round(255*random(1));//greenを置き換え | |
} | |
else if(rourette < 0.75){ | |
col[2][CHANGED_SHAPE_INDEX] = round(255*random(1));//blueを置き換え | |
} | |
else if(rourette < 1.0){ | |
col[3][CHANGED_SHAPE_INDEX] = round(255*random(1));//alphaを置き換え | |
} | |
} | |
else{ | |
CHANGED_POINT_INDEX = round((MAX_POINTS)*random(1))-1; | |
if(CHANGED_POINT_INDEX < 0){CHANGED_POINT_INDEX = 0;} | |
if(rourette<1.5){ | |
pt[CHANGED_POINT_INDEX][CHANGED_SHAPE_INDEX] = round(IWIDTH*random(1));//x座標を置き換え | |
} | |
else{ | |
pt[CHANGED_POINT_INDEX*2][CHANGED_SHAPE_INDEX] = round(IHEIGHT*random(1));//y座標を置き換え | |
} | |
} | |
} | |
/*評価関数を計算*/ | |
void compute_fitness(){ | |
data_test = new color[IWIDTH*IHEIGHT]; | |
loadPixels();//ウィンドウ全体のピクセル情報を取得 | |
int fitness_red = 0;//赤色の差分 | |
int fitness_green = 0;//緑色の差分 | |
int fitness_blue = 0;//青色の差分 | |
//int fitness_alpha = 0;//アルファチャンネルの差分 | |
/*TEST画面のピクセル情報を抽出*/ | |
for(int y = 50 ; y < 50+IHEIGHT;y++){ | |
for(int x = 550;x < 550+IWIDTH ; x++){ | |
data_test[(y-50)*IWIDTH + (x-550)] = get(x,y); | |
} | |
} | |
/*入力画像とTEST画面のピクセル情報の差分を計算*/ | |
for(int k = 0 ; k < IWIDTH*IHEIGHT; k++){ | |
fitness_red += abs(red(data_input[k]) - red(data_test[k])); | |
fitness_green += abs(green(data_input[k]) - green(data_test[k])); | |
fitness_blue += abs(blue(data_input[k]) - blue(data_test[k])); | |
//fitness_alpha += abs(alpha(data_input[k]) - alpha(data_test[k])); | |
} | |
//FITNESS_TEST = float(fitness_red + fitness_blue + fitness_green + fitness_alpha);//差分を格納 | |
FITNESS_TEST = float(fitness_red + fitness_blue + fitness_green);//差分を格納 | |
} |
0 件のコメント:
コメントを投稿