2018年6月10日日曜日

【Processing】遺伝的アルゴリズムを使った画像生成 Part2

★うさぎの画像を遺伝的アルゴリズムで作ってみたpart2


Part1で作ったアルゴリズムだとデザイン的な面白さがあまりないので、ちょっと改良してみました。

Part1では、遺伝子の総当たりの組み合わせと残す組み合わせをそれぞれ別々の画像範囲で表示させていましたが、それらを一つに絞りました。

さらに、多角形を一定の大きさの円にすることで、モザイク画のような絵を作る事ができます。

まあ、モザイク画を作るだけなら画像処理ソフトでも可能なので使える場面はあまりないかも知れませんが。

アルゴリズムの中身の仕組みを表した簡単な資料をこちらに添付しておきます↓

<注意点>

・入力画像は200x200の解像度で、sketchフォルダ直下に置きます。
・画像の名前はrabit.jpgとなってますので、画像の名前を合わせるか、sketchの該当箇所の修正をしてください。

以下、ソースです↓

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 件のコメント:

コメントを投稿

【Grasshopper_110】縦長長方形の範囲内で大きさを変化させる

今回は、縦長の四角形にひし形を配置し、真ん中より少し上の位置を基準に大きさを変化させるサンプルを作ってみました。 四角形の枠に近いところを同じ大きさに揃えるところが今回のポイントです。