★うさぎの画像を遺伝的アルゴリズムで作ってみたpart2
Part1では、遺伝子の総当たりの組み合わせと残す組み合わせをそれぞれ別々の画像範囲で表示させていましたが、それらを一つに絞りました。
さらに、多角形を一定の大きさの円にすることで、モザイク画のような絵を作る事ができます。
まあ、モザイク画を作るだけなら画像処理ソフトでも可能なので使える場面はあまりないかも知れませんが。
<注意点>
・入力画像は200x200の解像度で、sketchフォルダ直下に置きます。・画像の名前はrabit.jpgとなってますので、画像の名前を合わせるか、sketchの該当箇所の修正をしてください。
以下、ソースです↓
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 INPUT_FILE ="rabit.jpg"; //入力画像のファイル名 | |
int INPUT_X = 50;//入力画像の左上x | |
int INPUT_Y = 50;//入力画像の左上y | |
int IMAGE_WIDTH = 0;//画像のピクセル幅 | |
int IMAGE_HEIGHT = 0;//画像のピクセル高さ | |
int COUNTER_TOTAL = 0;//計算の回数 | |
int COUNTER_MUTATION = 0;//突然変異の回数 | |
int MAX_SHAPES = 50;//一度に計算する多角形の個数 | |
int X_Y = 2; | |
int CHANGED_SHAPE_INDEX = 0;//突然変異のとき、50個の多角形のうち一つを指定する番号 | |
int DIA = 5;//四角形の縦横長さ | |
int COUNT = 1;//スクリーンショットの連番用変数 | |
int MS = 0;//経過時間 | |
int TIME_DIFF;//時間差分 | |
int TIME_TOTAL1,TIME_TOTAL2;//停止時間、再開時間の合計値 | |
int EVOLVE_X = 0;//EVOLVE画面の左上x | |
int EVOLVE_Y = 0;//EVOLVE画面の左上y | |
int NORM_COEF = 0; // maximum distance between black and white images | |
boolean FLAG = true;//ENTERキーのON/OFF判定スイッチ | |
Float FITNESS_INIT = 999923404.0;//初期値 | |
Float FITNESS_EVOLVE = FITNESS_INIT;//EVOVE画面の評価関数 | |
Float FITNESS_BEST = FITNESS_INIT;//TEMP画面の評価関数 | |
Float FITNESS_RATIO = 0.0; // pixel match: 0% worst - 100% best | |
/*配列*/ | |
Integer[][] DNA_POINT = new Integer[X_Y][MAX_SHAPES];//4角形の4隅の点x,y座標を確保する配列 0:x1,1:x2,2:x3,3:x4,4:y1,5:y2,6:y3,7:y4 | |
Integer[][] DNA_TEMP_POINT = new Integer[X_Y][MAX_SHAPES]; | |
Integer[][] DNA_COLOR = new Integer[4][MAX_SHAPES];//RGBAの4チャンネル分の配列を確保 0:R,1:G,3:B,4:A | |
Integer[][] DNA_TEMP_COLOR = new Integer[4][MAX_SHAPES]; | |
color[] PIXEL_INPUT;//入力画面のピクセル情報を格納する配列 | |
color[] PIXEL_EVOLVE;//突然変異後のEVOLVE画面のピクセル情報を格納する配列 | |
color[] PIXEL_TEMP;//突然変異前のEVOLVE画面のピクセル情報を一時保存する配列 | |
/*アレイリスト*/ | |
ArrayList<Integer>STOP_TIME = new ArrayList<Integer>(); | |
ArrayList<Integer>START_TIME = new ArrayList<Integer>(); | |
/*フォント*/ | |
PFont FONT; | |
String[] FONT_LIST = PFont.list();//パソコン内部のフォントリストを作成 | |
void setup(){ | |
size(920,900);//ウィンドウサイズ | |
/*入力画像の表示*/ | |
INPUT_IMAGE = loadImage(INPUT_FILE);//入力画像の読み込み | |
IMAGE_WIDTH = INPUT_IMAGE.width;//入力画像の幅を取得 | |
IMAGE_HEIGHT = INPUT_IMAGE.height;//入力画像の高さを取得 | |
//SUBPIXELS = IWIDTH*IHEIGHT*DEPTH; | |
NORM_COEF = IMAGE_WIDTH * IMAGE_HEIGHT * 3 * 255; | |
//size(IMAGE_WIDTH * 2 + INPUT_X * 3,IMAGE_HEIGHT + INPUT_Y * 3); | |
background(0);//背景を黒色 | |
smooth(); | |
frameRate(60); | |
image(INPUT_IMAGE, INPUT_X, INPUT_Y); //画像の左上を(50,100)に指定して表示 | |
/*文字の表示*/ | |
//FONT = createFont("Arial",15); //フォントリストの中から選ぶ この際に最大サイズを決める | |
//textFont(FONT); //設定したフォントを使用 | |
textSize(20); //サイズを最終決定 | |
textAlign(CENTER); | |
text("Input_Image",INPUT_X + IMAGE_WIDTH / 2,INPUT_Y + IMAGE_HEIGHT + 20); | |
text("Output_Image",(INPUT_X * 2) + IMAGE_WIDTH * 1.5,INPUT_Y + IMAGE_HEIGHT + 20); | |
/*入力画像のピクセル情報の取得*/ | |
INPUT_IMAGE.loadPixels();//入力画像のピクセル情報を取得 | |
PIXEL_INPUT = new color[IMAGE_WIDTH * IMAGE_HEIGHT];//色情報を格納するカラー型配列を作成 | |
for (int i = 0; i < IMAGE_WIDTH * IMAGE_HEIGHT; i++) {//入力画像のカラー情報を格納(RGBA) | |
PIXEL_INPUT[i] = INPUT_IMAGE.pixels[i]; | |
} | |
/*CANVASの作成*/ | |
stroke(125); | |
strokeWeight(2); | |
fill(255,255,255); | |
EVOLVE_X = INPUT_X * 2 + IMAGE_WIDTH; | |
EVOLVE_Y = INPUT_Y; | |
rect(EVOLVE_X,EVOLVE_Y,IMAGE_WIDTH,IMAGE_HEIGHT);//EVOLVE画面 | |
/*初期のDNAを作成*/ | |
init_dna(DNA_POINT,DNA_COLOR); | |
/*EVOLVE画面に初期DNAを描画*/ | |
drawDNA(DNA_POINT,DNA_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(){ | |
TIME_TOTAL1 = 0;//初期化 | |
TIME_TOTAL2 = 0;//初期化 | |
for(int i = 0; i < STOP_TIME.size(); i++){//停止時間の和 | |
TIME_TOTAL1 += (STOP_TIME.get(i)); | |
} | |
for(int j = 0; j < START_TIME.size(); j++){//再開時間の和 | |
TIME_TOTAL2 += (START_TIME.get(j)); | |
} | |
TIME_DIFF = TIME_TOTAL2 - TIME_TOTAL1;//差分をとる | |
} | |
void draw(){ | |
MS = millis() - TIME_DIFF;//経過時間を取得 | |
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,550,49);//テキスト表示をいったん消す | |
fill(255,255,255);//文字の色 | |
text("Fitness:" + FITNESS_RATIO + "%" , 5, 20);//文字 | |
text("Total:" + COUNTER_TOTAL , 170, 20);//文字 | |
text("Mutation:" + COUNTER_MUTATION , 320, 20);//文字 | |
text("Elapsed_Time:" + H + "h " + M + "m " + S + "s " , 5, 40);//文字 | |
evolve(); | |
} | |
/*初期DNAの作成*/ | |
void init_dna(Integer[][] pt,Integer[][] col){ | |
for (int i = 0; i < MAX_SHAPES; i++){//50個分の四角形 | |
pt[0][i] = (DIA/2) + round(random(1) * (IMAGE_WIDTH - DIA));//x座標をランダムに生成 | |
pt[1][i] = (DIA/2) + round(random(1) * (IMAGE_HEIGHT - DIA));//y座標をランダムに生成 | |
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][i] = int(red(c));//color型の赤を取得 | |
col[1][i] = int(green(c));//color型の緑を取得 | |
col[2][i] = int(blue(c));//color型の青を取得 | |
col[3][i] = int(alpha(c));//color型のアルファを取得 | |
} | |
} | |
/*EVOLVE画面を描画*/ | |
void drawDNA(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(EVOLVE_X,EVOLVE_Y); | |
ellipse(pt[0][x],pt[1][x],DIA,DIA); | |
popMatrix(); | |
} | |
} | |
/*DNAの4点の座標をコピー*/ | |
void copyDNA_point(Integer[][] dna_from, Integer[][] dna_to){ | |
for (int i = 0; i < MAX_SHAPES; i++){ | |
for (int j = 0; j < X_Y; j++){ | |
dna_to[j][i] = dna_from[j][i]; | |
} | |
} | |
} | |
/*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]; | |
} | |
} | |
/*世代交代*/ | |
void evolve(){ | |
PIXEL_TEMP = new color[IMAGE_WIDTH * IMAGE_HEIGHT];//一時確保用のカラー配列 | |
loadPixels();//ウィンドウ全体のピクセル情報を取得 | |
//突然変異前のEVOLVE画面のピクセル情報を抽出してTEMPに一時保存 | |
for(int y = EVOLVE_Y; y < EVOLVE_Y + IMAGE_HEIGHT; y++){ | |
for(int x = EVOLVE_X; x < EVOLVE_X + IMAGE_WIDTH; x++){ | |
PIXEL_TEMP[(y - EVOLVE_Y) * IMAGE_WIDTH + (x - EVOLVE_X)] = get(x,y); | |
} | |
} | |
/*EVOLVEからTEMPにDNAをコピー(一時保存)*/ | |
copyDNA_point(DNA_POINT,DNA_TEMP_POINT); | |
copyDNA_color(DNA_COLOR,DNA_TEMP_COLOR); | |
mutateDNA(DNA_POINT,DNA_COLOR);//初期DNA(50個の多角形)に対し、変更(突然変異)を加えていく | |
drawDNA(DNA_POINT,DNA_COLOR);//突然変異後のデータでEVOLVE画面の描画(どんどん上書き) | |
compute_fitness();//評価関数の計算 | |
if(FITNESS_EVOLVE < FITNESS_BEST){//FITNESSがより小さくなったら | |
FITNESS_BEST= FITNESS_EVOLVE;//FITBESS_TEMPを更新 | |
FITNESS_RATIO = round(1000*(1.0-(FITNESS_BEST/ NORM_COEF)))/10.0; //評価関数の割合を計算 小数点一桁にするため、10倍の四捨五入してから10で除算 | |
COUNTER_MUTATION ++; | |
} | |
else{//FITNESSが小さくならないときは | |
/*EVOLVE画面を突然変異前に戻す*/ | |
for(int y = EVOLVE_Y; y < EVOLVE_Y + IMAGE_HEIGHT; y++){ | |
for(int x = EVOLVE_X; x < EVOLVE_X + IMAGE_WIDTH; x++){ | |
set(x,y,PIXEL_TEMP[(y - EVOLVE_Y) * IMAGE_WIDTH + (x - EVOLVE_X)]); | |
} | |
} | |
/*TEMPからEVOLVEにDNAを戻す*/ | |
copyDNA_point(DNA_TEMP_POINT,DNA_POINT); | |
copyDNA_color(DNA_TEMP_COLOR,DNA_COLOR); | |
} | |
COUNTER_TOTAL++; | |
} | |
/*突然変異*/ | |
void mutateDNA(Integer[][] pt, Integer[][] col){ | |
CHANGED_SHAPE_INDEX = round((MAX_SHAPES - 1) * random(1)); | |
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{ | |
if(ROURETTE < 1.5){ | |
pt[0][CHANGED_SHAPE_INDEX] = (DIA/2) + round((IMAGE_WIDTH - DIA) * random(1));//x座標を置き換え | |
} | |
else{ | |
pt[1][CHANGED_SHAPE_INDEX] =(DIA/2) + round((IMAGE_HEIGHT - DIA) * random(1));//y座標を置き換え | |
} | |
} | |
} | |
/*評価関数を計算*/ | |
void compute_fitness(){ | |
PIXEL_EVOLVE = new color[IMAGE_WIDTH * IMAGE_HEIGHT]; | |
loadPixels();//ウィンドウ全体のピクセル情報を取得 | |
int fitness_red = 0;//赤色の差分 | |
int fitness_green = 0;//緑色の差分 | |
int fitness_blue = 0;//青色の差分 | |
//int fitness_alpha = 0;//アルファチャンネルの差分 | |
/*EVOLVE画面のピクセル情報を抽出*/ | |
for(int y = EVOLVE_Y; y < EVOLVE_Y + IMAGE_HEIGHT; y++){ | |
for(int x = EVOLVE_X; x < EVOLVE_X + IMAGE_WIDTH; x++){ | |
PIXEL_EVOLVE[(y - EVOLVE_Y) * IMAGE_WIDTH + (x - EVOLVE_X)] = get(x,y); | |
} | |
} | |
/*入力画像とEVOLVE画面のピクセル情報の差分を計算*/ | |
for(int k = 0; k < IMAGE_WIDTH * IMAGE_HEIGHT; k++){ | |
fitness_red += abs(red(PIXEL_INPUT[k]) - red(PIXEL_EVOLVE[k])); | |
fitness_green += abs(green(PIXEL_INPUT[k]) - green(PIXEL_EVOLVE[k])); | |
fitness_blue += abs(blue(PIXEL_INPUT[k]) - blue(PIXEL_EVOLVE[k])); | |
//fitness_alpha += abs(alpha(data_input[k]) - alpha(data_test[k])); | |
} | |
//FITNESS_EVOLVE = float(fitness_red + fitness_blue + fitness_green + fitness_alpha);//差分を格納 | |
FITNESS_EVOLVE = float(fitness_red + fitness_blue + fitness_green);//差分を格納 | |
} |
0 件のコメント:
コメントを投稿