遺伝的アルゴリズムで画像をベクトル化します

そのため、週末には楽しい時間を過ごす必要があるため、遺伝的アルゴリズムを使用して画像をベクトル化してみてください。





挑戦する



半透明のポリゴンから可能な限り画像を半透明にします。



まず第一に、この問題を解決するために遺伝的アルゴリズムを使用しても、リードタイムが非常に長いため実用的な利点はなく、すべてが楽しみのために行われると言いたいです。



デモンストレーション



最終的に9月に近づくことに関連して、Dr。Houseの写真がプログラムの例として選ばれました。



したがって、 20時間で100十角形からDr. Houseが作成されました。





ソースデータ



このタスクの遺伝子は何でしょうか? これらは、ポリゴンのポイントの座標(以降、ポリゴンと呼びます)とその色になります。 交雑育種は使用せず、突然変異のみを使用します(これは完全に受け入れられます)。 奇妙に思えるかもしれませんが、人口は1人の個人で構成され、ポリゴンが描かれた絵になります。



デバイスの機能は、すべてのピクセルの色誤差の合計になります。 ピクセルのエラーは次のように定義されます

R * R + G * G + B * B、ここでRは元の画像のピクセルの赤成分と結果の差で、GとBは緑と青です。 関数の値が小さいほど、エラーが小さくなり、より良い画像が得られます。



コード



ポリゴンポイントクラスを定義します。

[ Serializable ]

public class Point: ICloneable

{

public int X { get ; set ; }

public int Y { get ; set ; }



public void SetRandom()

{

X = Helper.Rand.Next(0, Helper.Width);

Y = Helper.Rand.Next(0, Helper.Height);

}



public void Mutate(Workarea workarea)

{

if (Helper.Rand.NextDouble() <= Helper.MutationPointMoveMaxChance)

{

SetRandom();

workarea.IsChange = true ;

}



if (Helper.Rand.NextDouble() <= Helper.MutationPointMoveMiddleChance)

{

X = Math .Min( Math .Max(0, X + Helper.Rand.Next(-Helper.MiddleRange, Helper.MiddleRange + 1)), Helper.Width);

Y = Math .Min( Math .Max(0, Y + Helper.Rand.Next(-Helper.MiddleRange, Helper.MiddleRange + 1)), Helper.Height);

workarea.IsChange = true ;

}



if (Helper.Rand.NextDouble() <= Helper.MutationPointMoveNearChance)

{

X = Math .Min( Math .Max(0, X + Helper.Rand.Next(-Helper.NearRange, Helper.NearRange + 1)), Helper.Width);

Y = Math .Min( Math .Max(0, Y + Helper.Rand.Next(-Helper.NearRange, Helper.NearRange + 1)), Helper.Height);

workarea.IsChange = true ;

}

}



public object Clone()

{

return new Point { X = X, Y = Y };

}

}




* This source code was highlighted with Source Code Highlighter .






ポイントにはX座標とY座標があります。 SetRandomプロシージャは、画像のサイズに応じてポイントランダム座標を設定します。 Mutateプロシージャは3つの部分に分割され、各部分は事前に決定された損失の確率に従って実行されます。 最初は、写真のどこにでも移動するポイントにチャンスを与えます。 2番目の部分はポイントを平均距離に移動し、最大20ピクセルまで持っていることがわかります。 3番目の部分は、わずかな距離を移動する機会を与えます。最大3ピクセルまで移動できます。



次は、ポリゴンの色を担当するブラシクラスです。

[ Serializable ]

public class Brush: ICloneable

{

public int A { get ; set ; }

public int R { get ; set ; }

public int G { get ; set ; }

public int B { get ; set ; }



public void SetRandom()

{

A = Helper.Rand.Next(30, 60);

R = Helper.Rand.Next(0, 256);

G = Helper.Rand.Next(0, 256);

B = Helper.Rand.Next(0, 256);

}



public void Mutate(Workarea workarea)

{

if (Helper.Rand.NextDouble() <= Helper.MutationBrushAChance)

{

A = Helper.Rand.Next(30, 60);

workarea.IsChange = true ;

}



if (Helper.Rand.NextDouble() <= Helper.MutationBrushRChance)

{

R = Helper.Rand.Next(0, 256);

workarea.IsChange = true ;

}



if (Helper.Rand.NextDouble() <= Helper.MutationBrushGChance)

{

G = Helper.Rand.Next(0, 256);

workarea.IsChange = true ;

}



if (Helper.Rand.NextDouble() <= Helper.MutationBrushBChance)

{

B = Helper.Rand.Next(0, 256);

workarea.IsChange = true ;

}

}



public object Clone()

{

return new Brush

{

A = A,

R = R,

G = G,

B = B

};

}

}




* This source code was highlighted with Source Code Highlighter .






ブラシには、 ARGBのコンポーネントカラーがあります。 SetRandomは、それらをランダムな値で初期化します。 突然変異の手順では、各コンポーネントは突然変異の確率に従ってランダムに変化します。



ポリゴンのクラスに行きます:

[ Serializable ]

public class Polygon: ICloneable

{

public List <Point> Points { get ; set ; }

public Brush Brush { get ; set ; }



public void SetRandom()

{

Points = new List <Point>();



Point center = new Point();

center.SetRandom();



for ( int i = 0; i < Helper.MinPointsPerPolygon; i++)

{

Point point = new Point();

point.X = Math .Min( Math .Max(0, center.X + Helper.Rand.Next(-3, 4)), Helper.Width);

point.Y = Math .Min( Math .Max(0, center.X + Helper.Rand.Next(-3, 4)), Helper.Height);

Points.Add(point);

}



Brush = new Brush();

Brush.SetRandom();

}



public void Mutate(Workarea workarea)

{

if (Helper.Rand.NextDouble() <= Helper.MutationPolygonAddPointChance)

{

AddPoint();

workarea.IsChange = true ;

}



if (Helper.Rand.NextDouble() <= Helper.MutationPolygonDelPointChance)

{

DelPoint();

workarea.IsChange = true ;

}



Brush.Mutate(workarea);

Points.ForEach(p => p.Mutate(workarea));

}



private void AddPoint()

{

if (Points.Count < Helper.MaxPointsPerPolygon)

{

Point point = new Point();

int index = Helper.Rand.Next(1, Points.Count - 1);

Point p1 = Points[index - 1];

Point p2 = Points[index];

point.X = (p1.X + p2.X) / 2;

point.Y = (p1.Y + p2.Y) / 2;

Points.Insert(index, point);

}

}



private void DelPoint()

{

if (Points.Count > Helper.MinPointsPerPolygon)

{

int index = Helper.Rand.Next(0, Points.Count);

Points.RemoveAt(index);

}

}



public void Draw( Graphics g)

{

using ( SolidBrush brush = new SolidBrush (Color.FromArgb(Brush.A, Brush.R, Brush.G, Brush.B)))

{

System.Drawing.Point[] points = Points.Select(p => new System.Drawing.Point(pX,pY)).ToArray();

g.FillPolygon(brush, points);

}

}



public object Clone()

{

Polygon newpolygon = new Polygon();

newpolygon.Brush = Brush.Clone() as Brush;

newpolygon.Points = new List <Point>();

Points.ForEach(p => newpolygon.Points.Add(p.Clone() as Point));

return newpolygon;

}

}




* This source code was highlighted with Source Code Highlighter .






ポリゴンには、ポイントの配列とブラシ(色)が含まれます。 SetRandomプロシージャは、ブラシを初期化し、ランダムに選択されたポイントの周りに最小数のポイントを描画します。 ポリゴンの突然変異は、ブラシとポリゴンの各ポイントの突然変異であり、何らかの確率で、いくつかのポイントを追加または削除できます。 Drawプロシージャは、ポリゴンを描画します。



最後に、最後のメインクラスは、ポリゴンを含むワークスペースです。

[ Serializable ]

public class Workarea: ICloneable

{

public List <Polygon> Polygons { get ; set ; }



[XmlIgnore]

public bool IsChange { get ; set ; }



public void SetRandom()

{

Polygons = new List <Polygon>();



for ( int i = 0; i < Helper.MinPolygons; i++)

{

AddPolygon();

}



IsChange = true ;

}



public void Mutate()

{

if (Helper.Rand.NextDouble() <= Helper.MutationWorkareaAddPolygonChance)

{

AddPolygon();

IsChange = true ;

}



if (Helper.Rand.NextDouble() <= Helper.MutationWorkareaDelPolygonChance)

{

DelPolygon();

IsChange = true ;

}



Polygons.ForEach(p => p.Mutate( this ));

}



private void AddPolygon()

{

if (Polygons.Count < Helper.MaxPolygons)

{

Polygon polygon = new Polygon();

polygon.SetRandom();

Polygons.Add(polygon);

}

}



private void DelPolygon()

{

if (Polygons.Count > Helper.MinPolygons)

{

int index = Helper.Rand.Next(0, Polygons.Count);

Polygons.RemoveAt(index);

}

}



public double Fitness(Color[,] colors)

{

double fitness = 0;

Bitmap img = Draw();

FastBitmap fastimg = new FastBitmap(img);

for ( int i = 0; i < Helper.Width; i++)

{

for ( int j = 0; j < Helper.Height; j++)

{

Color c1 = fastimg.GetPixel(i, j);

Color c2 = colors[i, j];

int r = c1.R - c2.R;

int g = c1.G - c2.G;

int b = c1.B - c2.B;

fitness += r * r + g * g + b * b;

}

}

fastimg.Release();

img.Dispose();

return fitness;

}



public Bitmap Draw()

{

Bitmap img = new Bitmap (Helper.Width, Helper.Height);

using ( Graphics g = Graphics .FromImage(img))

{

g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

g.Clear(Color.Black);



Polygons.ForEach(p => p.Draw(g));

}

return img;

}



public object Clone()

{

Workarea newarea = new Workarea();

newarea.Polygons = new List <Polygon>();

Polygons.ForEach(p => newarea.Polygons.Add(p.Clone() as Polygon));

return newarea;

}

}




* This source code was highlighted with Source Code Highlighter .






IsChangeプロパティの真実は、突然変異がどこかで発生したことを示しています。 SetRandomは、ポリゴンの最小初期数を初期化します。 突然変異は各ポリゴンの突然変異を引き起こし、ポリゴンは特定の確率で追加または削除できます。 Function Fitnessはデバイスの機能を計算し、元の画像の色の配列を渡します。 さて、 Draw関数はレンダリングされた画像を返します。



アルゴリズム自体は次のようになります。

private void Start()

{

if (workarea == null )

{

workarea = new Workarea();

workarea.SetRandom();

}



while (isRunning)

{

Workarea newarea;

lock (workarea)

{

newarea = workarea.Clone() as Workarea;

}

newarea.Mutate();



if (newarea.IsChange)

{

double newfitness = newarea.Fitness(sourceColors);



if (newfitness <= fitness)

{

lock (workarea)

{

workarea = newarea;

}

fitness = newfitness;

}

}

}

}




* This source code was highlighted with Source Code Highlighter .






初期母集団を初期化してからコピーして突然変異させ、新しい母集団の適応関数を計算し、それが現在の母集団よりも小さい場合(思い出してください、関数の値が低いほど良い)、現在の母集団を新しい、より良い母集団で上書きします。



設定が保存されるヘルパークラスがまだあります。

public static class Helper

{

public static readonly Random Rand = new Random ();



public static int Width = 0;

public static int Height = 0;



public static int MinPointsPerPolygon = 3;

public static int MaxPointsPerPolygon = 10;



public static int MinPolygons = 0;

public static int MaxPolygons = 100;



public static int NearRange = 3;

public static int MiddleRange = 20;



public static double MutationPointMoveMaxChance = 0.0007;

public static double MutationPointMoveMiddleChance = 0.0007;

public static double MutationPointMoveNearChance = 0.0007;



public static double MutationBrushAChance = 0.0007;

public static double MutationBrushRChance = 0.0007;

public static double MutationBrushGChance = 0.0007;

public static double MutationBrushBChance = 0.0007;



public static double MutationPolygonAddPointChance = 0.0007;

public static double MutationPolygonDelPointChance = 0.0007;



public static double MutationWorkareaAddPolygonChance = 0.0014;

public static double MutationWorkareaDelPolygonChance = 0.0007;

}




* This source code was highlighted with Source Code Highlighter .






ここに、元の画像の幅と高さ(読み込み中に初期化された)、ポリゴン内のポイントの最小数と最大数、ポリゴンの最小数と最大数、各コンポーネントの突然変異確率(単位は100%の突然変異)、およびポイントが移動できる距離を保存します。

ここでプロジェクト全体をマージできます



UPD:カルマに感謝し、ブログのアルゴリズムに移しました。




All Articles