forked from: forked from: フラクタルで画像を描画する
フラクタルで画像を描画
パクリ元ネタ:
fladdict » コンピューターに絵画を描かせる
http://fladdict.net/blog/2009/05/computer-painting.html
標準偏差:
http://www.cap.or.jp/~toukei/kandokoro/html/14/14_2migi.htm
画像の読み込み処理:
http://wonderfl.kayac.com/code/3fb2258386320fe6d2b0fe17d6861e7da700706a
RGB->HSB変換:
http://d.hatena.ne.jp/flashrod/20060930#1159622027
/**
* Copyright alterna_in ( http://wonderfl.net/user/alterna_in )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/Qq2f
*/
// forked from alternadotin's forked from: フラクタルで画像を描画する
// forked from fumix's フラクタルで画像を描画する
/**
フラクタルで画像を描画
パクリ元ネタ:
fladdict » コンピューターに絵画を描かせる
http://fladdict.net/blog/2009/05/computer-painting.html
標準偏差:
http://www.cap.or.jp/~toukei/kandokoro/html/14/14_2migi.htm
画像の読み込み処理:
http://wonderfl.kayac.com/code/3fb2258386320fe6d2b0fe17d6861e7da700706a
RGB->HSB変換:
http://d.hatena.ne.jp/flashrod/20060930#1159622027
**/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.text.TextField;
/**
* ...
* @author DefaultUser (Tools -> Custom Arguments...)
*/
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#ffffff")]
public class Main extends Sprite
{
private const IMAGE_URL:String = "http://farm4.static.flickr.com/3639/3538831894_cca4aabd68.jpg";
//標準偏差の閾値。小さくすると細かくなるけど、小さすぎるとただのモザイクみたくなる。
private const THRESHOLD:Number = .001;
private var fillRectangleArray:Array;
private var image:Bitmap;
private var imageData:BitmapData;
private var _canvas:Sprite;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
//画像の読み込み
var req:URLRequest = new URLRequest(IMAGE_URL);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
loader.load( req, new LoaderContext(true));
// take a capture after 10 sec
Wonderfl.capture_delay( 120 );
}
//画像読み込み後の処理
function loadComplete(e:Event = null):void
{
e.target.removeEventListener(Event.COMPLETE, loadComplete);
image = e.target.loader.content as Bitmap;
imageData = image.bitmapData;
//キャンバス用スプライト
_canvas = new Sprite;
var p:RectanglePiece = new RectanglePiece();
p.x0 = 0;
p.y0 = 0;
p.x1 = imageData.width;
p.y1 = imageData.height;
p.c = 0;
//フラクタルデータ保持用配列に初期値挿入
fillRectangleArray = new Array(p);
addChild(_canvas);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
//ループ
private function onEnterFrame(e:Event):void
{
//フラクタル処理終了
if (fillRectangleArray.length < 1) {
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
var tx:TextField = new TextField();
tx.text = '終了';
tx.textColor = 0xFFFFFF;
addChild(tx);
}else {
//フラクタルデータ保持用配列から1つ取り出す
var rect:RectanglePiece = fillRectangleArray.shift();
var cArray:Array = deviationLogic(rect.x0, rect.y0, rect.x1, rect.y1);
rect.c = cArray[0];
var halfWidth:Number = (rect.x1 - rect.x0) / 2;
var halfHeight:Number = (rect.y1 - rect.y0) / 2;
// 指定した矩形内の輝度の標準偏差値が閾値以上なら2分木して処理続行
if (rect.c > THRESHOLD && (halfWidth > 2 || halfHeight > 2)) {
//矩形を書くよ
//_canvas.graphics.lineStyle(0, 0xAAAAAA);
_canvas.graphics.beginFill(cArray[1]);
_canvas.graphics.drawRect(rect.x0, rect.y0, (rect.x1 - rect.x0), (rect.y1 - rect.y0));
//矩形を2分割してフラクタルデータ保持用配列に突っ込む
var rect0:RectanglePiece = new RectanglePiece();
var rect1:RectanglePiece = new RectanglePiece();
if (halfWidth > halfHeight) {
rect0.x0 = rect.x0;
rect0.y0 = rect.y0;
rect0.x1 = rect.x0+halfWidth;
rect0.y1 = rect.y1;
fillRectangleArray.push(rect0);
rect1.x0 = rect.x0+halfWidth;
rect1.y0 = rect.y0;
rect1.x1 = rect.x1;
rect1.y1 = rect.y1;
fillRectangleArray.push(rect1);
}else {
rect0.x0 = rect.x0;
rect0.y0 = rect.y0;
rect0.x1 = rect.x1;
rect0.y1 = rect.y0+halfHeight;
fillRectangleArray.push(rect0);
rect1.x0 = rect.x0;
rect1.y0 = rect.y0+halfHeight;
rect1.x1 = rect.x1;
rect1.y1 = rect.y1;
fillRectangleArray.push(rect1);
}
}
}
}
/**
* 指定した矩形間の輝度の標準偏差を求める
* @param x0 左上のx座標
* @param y0 左上のy座標
* @param x1 右下のx座標
* @param y1 右下のy座標
* @return 標準偏差値とカラーの平均
*/
private function deviationLogic(x0:Number,y0:Number,x1:Number,y1:Number):Array {
var rgb:uint = 0;
var r:uint = 0;
var g:uint = 0;
var b:uint = 0;
var hsb:Array = new Array();
var bArray:Array = new Array();
var br:Number = 0;
var av:Number = 0;
//輝度の平均を計算
for (var i:int = x0; i < x1;i++ ) {
for (var j:int = y0; j < y1; j++ ) {
rgb = imageData.getPixel(i, j);
r += (rgb >> 16) & 255;
g += (rgb >> 8) & 255;
b += rgb & 255;
hsb = uintRGBtoHSB(rgb);
br += hsb[2];
bArray.push(hsb[2]);
}
}
av = br / bArray.length;
r = r / bArray.length;
g = g / bArray.length;
b = b / bArray.length;
rgb = (r << 16) | (g << 8) | (b << 0);
//標準偏差を計算
br = 0;
for (i = 0; i < bArray.length; i++ ) {
br += (bArray[i] - av) *(bArray[i] - av);
}
return [Math.sqrt(br / bArray.length),rgb];
}
/**
*
* @param rgb RGB成分(uint)
* @return HSB配列([0]=hue, [1]=saturation, [2]=brightness)
*/
private function uintRGBtoHSB(rgb:uint):Array {
var r:uint = (rgb >> 16) & 255;
var g:uint = (rgb >> 8) & 255;
var b:uint = rgb & 255;
return RGBtoHSB(r, g, b);
}
/** RGBからHSBをつくる
* @param r 色の赤色成分(0~255)
* @param g 色の緑色成分(0~255)
* @param b 色の青色成分(0~255)
* @return HSB配列([0]=hue, [1]=saturation, [2]=brightness)
*/
private function RGBtoHSB(r:int, g:int, b:int):Array {
var cmax:Number = Math.max(r, g, b);
var cmin:Number = Math.min(r, g, b);
var brightness:Number = cmax / 255.0;
var hue:Number = 0;
var saturation:Number = (cmax != 0) ? (cmax - cmin) / cmax : 0;
if (saturation != 0) {
var redc:Number = (cmax - r) / (cmax - cmin);
var greenc:Number = (cmax - g) / (cmax - cmin);
var bluec:Number = (cmax - b) / (cmax - cmin);
if (r == cmax) {
hue = bluec - greenc;
} else if (g == cmax) {
hue = 2.0 + redc - bluec;
} else {
hue = 4.0 + greenc - redc;
}
hue = hue / 6.0;
if (hue < 0) {
hue = hue + 1.0;
}
}
return [hue, saturation, brightness];
}
}
}
/**
* ...
* @author DefaultUser (Tools -> Custom Arguments...)
*/
class RectanglePiece
{
public var x0:Number;
public var y0:Number;
public var x1:Number;
public var y1:Number;
public var c:Number;
public function RectanglePiece()
{
this.x0 = 0;
this.y0 = 0;
this.x1 = 0;
this.x1 = 0;
this.c = 0;
}
}