HEXA BLOG
ヘキサブログ
プログラム
32とか16とかその6
こんにちは。
シュンスケです![]()
前回、どれがキレイ(元画像に近い)なのかをプログラムで判断できれば、
数あるパターンから最適なものが見つけられるのではといった話がありました![]()
そこで、今回のアプローチは、GA![]()
ガーディアンエンジェルではありません。
ギャラクシーエンジェルでもありません。
Genetic Algorithmつまり、「遺伝的アルゴリズム」です![]()
大まかに説明すると、とある解を求めるにあたって、生物の進化のように、
解候補たちを組み替えながら、正解に近いものは残して、遠いものを淘汰し、
時には突然変異を起こすといった感じで、多くのパターンからより良いものを
導き出す処理方法です![]()
と、ここまで来て気付く方も多いと思いますが、正解に近いかどうか、それが今回でいうと、
元画像に近いかどうかということです。
その比較方法でお手軽なのがPSNR値を使うものなようなので、今回はそうします。
(ただし、人間の目を相手にするとなると、問題は多いようです)
そして今回、果敢にも全ピクセルの全チャンネルに関して、組み換え対象として
実装しました。
つまり、膨大なパターンがあるということです![]()
さて、以下実装です。
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
namespace Pic32bitTo16bitGA
{
class Program : Form
{
struct Gene
{
public static void cross(ref Gene a, ref Gene b)
{
if( a.buf == null || b.buf == null ) return;
// 2点交叉
var r = new Random();
var index1 = r.Next(0, (a.buf.Length - 1) / PIXEL_BYTES);
var index2 = r.Next((index1 + 1) / PIXEL_BYTES, b.buf.Length / PIXEL_BYTES);
for( int i = index1; i < index2; ++i ) {
var tmp0 = a.buf[i + 0];
var tmp1 = a.buf[i + 1];
var tmp2 = a.buf[i + 2];
var tmp3 = a.buf[i + 3];
a.buf[i + 0] = b.buf[i + 0];
a.buf[i + 1] = b.buf[i + 1];
a.buf[i + 2] = b.buf[i + 2];
a.buf[i + 3] = b.buf[i + 3];
b.buf[i + 0] = tmp0;
b.buf[i + 1] = tmp1;
b.buf[i + 2] = tmp2;
b.buf[i + 3] = tmp3;
}
}
// コンストラクタ
public Gene(int w, int h)
{
buf = new byte[w * h * PIXEL_BYTES];
psnr = -1;
}
// ランダムで埋める
public void fillRand()
{
// ランダムで色を埋める
var r = new Random();
r.NextBytes(buf);
}
// PSNRを算出して保持
public void calcPSNR(byte[] org)
{
psnr = -1;
if( org == null || buf == null || org.Length != buf.Length || org.Length == 0 ) return;
// MSE算出
ulong mse = 0;
for( int i = 0; i < org.Length; ++i ) {
long d = (buf[i] - org[i]);
mse += (ulong)(d * d);
}
mse /= (ulong)org.Length;
if( mse <= 0 ) return;
// PSNR算出
psnr = (10 * Math.Log10((double)(255 * 255) / (double)mse));
}
// 突然変異
public void mutation()
{
if( buf == null ) return;
var r = new Random();
var index = r.Next(0, buf.Length / PIXEL_BYTES);
buf[index + 0] = (byte)r.Next(0, 256);
buf[index + 1] = (byte)r.Next(0, 256);
buf[index + 2] = (byte)r.Next(0, 256);
buf[index + 3] = (byte)r.Next(0, 256);
}
// 値を設定
public void set(Gene g)
{
if( g.buf == null ) return;
if( buf == null || buf.Length != g.buf.Length ) {
buf = new byte[g.buf.Length];
}
Array.Copy(g.buf, buf, buf.Length);
psnr = g.psnr;
}
public byte[] buf;
public double psnr;
}
private const int PIXEL_BYTES = 4; //!< 1ピクセルあたりのバイト数
// エントリポイント
static void Main(string[] args)
{
// 入力受付
Console.WriteLine("please input picture path...");
var srcPath = Console.ReadLine();
// ファイル存在確認
if( !File.Exists(srcPath) ) {
Console.WriteLine("file not exists.");
return;
}
// 画像読み込み
byte[] buf = null;
int width = 0;
int height = 0;
using( var img = Image.FromFile(srcPath, true) as Bitmap ) {
if( img == null ) {
Console.WriteLine("file type is not support.");
return;
}
// メモリに保持
var dat = img.LockBits(new Rectangle(Point.Empty, img.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
width = img.Width;
height = img.Height;
buf = new byte[width * height * PIXEL_BYTES];
Marshal.Copy(dat.Scan0, buf, 0, buf.Length);
// 解放
img.UnlockBits(dat);
}
if( buf == null ) {
Console.WriteLine("buf load failed.");
return;
}
// フォーム開始
Application.Run(new Program(ref buf, width, height));
}
// コンストラクタ
public Program(ref byte[] buf, int w, int h)
{
if( buf == null ) return;
_org = buf;
_buf = new byte[buf.Length];
_img = new Bitmap(w, h);
// 第1世代生成
for( int i = 0; i < _curGen.Length; ++i ) {
_curGen[i] = new Gene(w, h);
_curGen[i].fillRand();
}
// フォームのサイズ設定
ClientSize = new Size(w, h * 2);
// フォント作成
_font = new Font(Font.Name, 10);
_fontBrush = new SolidBrush(ForeColor);
// 描画更新開始
_sec = 0;
_refreshTimer.Interval = 1000;
_refreshTimer.Tick += new EventHandler(refresh);
_refreshTimer.Start();
// 処理更新開始
_updateTimer.Interval = 1;
_updateTimer.Tick += new EventHandler(update);
_updateTimer.Start();
}
// 描画時処理
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if( _img == null ) return;
// 元画像描画
if( _org != null ) {
var newDat = _img.LockBits(new Rectangle(Point.Empty, _img.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(_org, 0, newDat.Scan0, _org.Length);
_img.UnlockBits(newDat);
e.Graphics.DrawImage(_img, 0, 0);
}
// 生成画像描画
if( _buf != null ) {
var newDat = _img.LockBits(new Rectangle(Point.Empty, _img.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(_buf, 0, newDat.Scan0, _buf.Length);
_img.UnlockBits(newDat);
e.Graphics.DrawImage(_img, 0, _img.Size.Height);
}
// フレーム数表示
e.Graphics.DrawString("sec : " + _sec, _font, _fontBrush, 0, 0);
// 世代数表示
e.Graphics.DrawString("gen : " + _genCnt, _font, _fontBrush, 100, 0);
}
// 描画更新
private void refresh(object sender, EventArgs e)
{
// 保持したトップを描画バッファに設定
if( _topGen.buf != null ) {
Array.Copy(_topGen.buf, _buf, _buf.Length);
}
// 画面をリフレッシュ
++_sec;
Invalidate();
}
// 処理更新
private void update(object sender, EventArgs e)
{
// 評価値算出
for( int i = 0; i < _curGen.Length; ++i ) {
_curGen[i].calcPSNR(_org);
}
// 評価値でソート
Array.Sort(_curGen, (a, b) => {
if( a.psnr == b.psnr ) return 0;
return (b.psnr > a.psnr) ? 1 : -1;
});
// 1位を保持
_topGen.set(_curGen[0]);
// 上位いくつかはそのまま残す
var newIndex = 0;
for( int i = 0; i < 2; ++newIndex, ++i ) {
_nextGen[newIndex].set(_curGen[i]);
}
while( newIndex < _nextGen.Length ) {
// 交叉対象1を選出(上位が選ばれやすいように)
_cross1Buf.set(_curGen[0]);
for( int i = 1; i < _curGen.Length; ++i ) {
var rate = _selectRnd.Next(0, 100);
if( rate < 10 ) {
_cross1Buf.set(_curGen[i]);
break;
}
}
// 交叉対象2を選出(上位が選ばれやすいように)
_cross2Buf.set(_curGen[1]);
for( int i = 2; i < _curGen.Length; ++i ) {
var rate = _selectRnd.Next(0, 100);
if( rate < 10 ) {
_cross2Buf.set(_curGen[i]);
break;
}
}
// 交叉
Gene.cross(ref _cross1Buf, ref _cross2Buf);
_nextGen[newIndex].set(_cross1Buf);
++newIndex;
if( _nextGen.Length <= newIndex ) break;
_nextGen[newIndex].set(_cross2Buf);
++newIndex;
if( _nextGen.Length <= newIndex ) break;
// 突然変異対象を選出
var mutIndex = _selectRnd.Next(0, _curGen.Length);
_nextGen[newIndex].set(_curGen[mutIndex]);
_nextGen[newIndex].mutation();
++newIndex;
if( _nextGen.Length <= newIndex ) break;
// そのままコピー
var copyIndex = _selectRnd.Next(0, _curGen.Length);
_nextGen[newIndex].set(_curGen[copyIndex]);
++newIndex;
if( _nextGen.Length <= newIndex ) break;
}
// 次世代を現世代に移す
for( int i = 0; i < _nextGen.Length; ++i ) {
_curGen[i].set(_nextGen[i]);
}
++_genCnt;
}
private Bitmap _img = null;
private byte[] _org = null;
private byte[] _buf = null;
private Timer _refreshTimer = new Timer();
private Timer _updateTimer = new Timer();
private Font _font = null;
private Brush _fontBrush = null;
private int _sec = 0;
private int _genCnt = 0;
private Gene[] _curGen = new Gene[64];
private Gene[] _nextGen = new Gene[64];
private Random _selectRnd = new Random();
private Gene _cross1Buf = new Gene();
private Gene _cross2Buf = new Gene();
private Gene _topGen = new Gene();
}
}
このままでは処理速度に難がありそうですが、実行してみます![]()
ウィンドウの中で、上が目指す画像で、下が完全ランダム値からはじめて、
最終的には同じ絵になって欲しい部分です。
はじめの方
:
:
約50分後
う、うーん…ダメだ![]()
さすがにパターンが多すぎたようです。
それに、交叉や突然変異の確率、評価処理もかなー、 見直しが必要そうです。
というか、そもそも全ピクセルランダムスタートで辿り着けるのか。。。
ただ、薄っすらと緑っぽく近づいてきてはいるので、アプローチの方向はまだ捨てずに
おこうと 思いますが、今回はここまで![]()
では![]()
CATEGORY
- about ヘキサ (166)
- 部活動 (6)
- CG (18)
- プロジェクトマネジメント (1)
- 研修 (5)
- 美学 (1)
- いいモノづくり道 (227)
- 採用 -お役立ち情報も- (149)
- プログラム (189)
- デザイン (99)
- ゲーム (273)
- 日記 (1,104)
- 書籍紹介 (113)
- その他 (889)
- 就活アドバイス (20)
- ラーメン (3)
- ライフハック (25)
- イベント紹介 (10)
- 料理 (23)
- TIPS (7)
- 怖い話 (3)
- サウンド (6)
- 子育て (1)
- 筋トレ (1)
- NicO (3)
- MakeS (9)
- 商品紹介 (21)
- アプリ紹介 (31)
- ソフトウェア紹介 (33)
- ガジェット紹介 (12)
- サイト紹介 (10)
- 研究・開発 (35)
- 回路図 (4)
- アナログゲーム (40)
- 交流会 (21)
- 報告会 (3)
- インフラ (25)
- グリとブラン (6)
- カメラ (9)
- クラフト (27)
- 部活 (14)
- 画伯 (15)
- カレー (6)
- 音楽(洋楽) (6)
- 映画・舞台鑑賞 (43)
- 飼育 (5)
- いぬ (8)
- ねこ (19)
ARCHIVE
- 2025年
- 2024年
- 2023年
- 2022年
- 2021年
- 2020年
- 2019年
- 2018年
- 2017年
- 2016年
- 2015年
- 2014年
- 2013年
- 2012年
- 2011年
- 2010年
- 2009年
- 2008年
- 2007年






