Beautifl - Flash Gallery

Thumbnail : フラクタルで画像を描画する
フラクタルで画像を描画する
fumix 2009-08-27 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

/**
フラクタルで画像を描画

パクリ元ネタ:
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 = "30", 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 = 0.1;

		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;			
		}
		
	}