画像をフラクタル化 forked from: 単位面積あたりの偏差がナントカカントカ

by undo forked from 単位面積あたりの偏差が小さい場合は赤く塗りつぶす forked from: pngで保存 (diff: 214)
♥7 | Line 199 | Modified 2009-11-20 15:58:03 | MIT License
play

ActionScript3 source code

/**
 * Copyright undo ( http://wonderfl.net/user/undo )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/61Nq
 */

<?xml version = "1.0" encoding = "utf-8"?>
<!-- forked from undo's 単位面積あたりの偏差が小さい場合は赤く塗りつぶす forked from: pngで保存 -->
<!-- forked from undo's pngで保存 forked from: モザイク forked from: モザイクがうまくいかない -->
<!-- forked from undo's モザイク forked from: モザイクがうまくいかない -->
<!-- forked from undo's モザイクがうまくいかない -->
<mx:Application xmlns:mx = "http://www.adobe.com/2006/mxml" layout = "absolute" applicationComplete = "init();">
	<mx:Script>
		<![CDATA[

		/*
		* やったーフラクタル化できたよー
		* 思いっきり同期処理なのでデカい画像だとしばらく止まります
		* 300〜500pixくらいで勘弁してください
		*/
		

import com.adobe.images.PNGEncoder;

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.FileFilter;

private var fr:FileReference = new FileReference();
private var ba:ByteArray = new ByteArray();
private var mosaic:ByteArray = new ByteArray();
private var rl:Loader = new Loader();
private var bmd:BitmapData;
private var bm:Bitmap = new Bitmap();
private var pbmd:BitmapData;
private var hensaThreshold:Number;

//フラクタルを囲む四角形の色
private var rectColor:uint = 0x888888;

private function init():void
{
	this.image.addChild(bm);
}

private function onBrowseBtnClick():void
{
	//画像ファイルを参照
	this.fr.addEventListener(Event.SELECT, onFileSelect);
	this.fr.browse([new FileFilter("Image", "*.jpg;*.gif;*.png")]);
}

private function onFileSelect(evt:Event):void
{
	this.fr.removeEventListener(Event.SELECT, onFileSelect);
	this.fr.addEventListener(Event.COMPLETE, onFileComplete);
	this.fr.load();
}

private function onFileComplete(evt:Event):void
{
	//画像をByteArrayに格納
	this.fr.removeEventListener(Event.COMPLETE, onFileComplete);
	this.ba = this.fr.data;
	this.rl.contentLoaderInfo.addEventListener(Event.COMPLETE, onPreviewLoadComp);
	this.rl.loadBytes(this.ba);
}

private function onPreviewLoadComp(evt:Event):void
{
	this.rl.contentLoaderInfo.removeEventListener(Event.COMPLETE, onPreviewLoadComp);
	this.bmd = new BitmapData(this.rl.contentLoaderInfo.width, this.rl.contentLoaderInfo.height, false);
	this.bmd.draw(rl);
	this.bm.bitmapData = this.bmd;
	this.bm.scaleX = this.bm.scaleY = 1;
	this.bm.scaleX = this.bm.scaleY = Math.min(this.width / this.bm.width, this.height / this.bm.height);
	//trace(this.bmd.width, this.bm.height);
}

private function onMosaicBtnClick():void
{
	if (this.ba.length > 0)
	{
		this.mosaic = null;
		this.mosaic = new ByteArray();
		this.pbmd = this.bmd.clone();
		this.bm.bitmapData = this.pbmd;
		startMosaic();
		this.mosaic = PNGEncoder.encode(this.pbmd);
	}
	else
	{
		//画像を参照してね
	}
}



//
//画像をMosaicする
//
private function startMosaic(p:Point = null, w:Number = -1, h:Number = -1):void
{
	if(p == null)p = new Point(0,0);
	if(w == -1)w = this.bmd.width;
	if(h == -1)h = this.bmd.height;

	var t:int = Math.floor(Number(this.time.text));
	this.hensaThreshold = Math.floor(Number(this.thr.text));
	var endFlg:Boolean = false;

	if(t > w || t > h || w == 1 || h == 1)
	{
		//wもしくはhが最小値以下になったら普通に平均出して終了
		endFlg = true;
	}
	var newColor:uint = 0;
	var newAlpha:uint = 0;
	var newRed:uint = 0;
	var newGreen:uint = 0;
	var newBlue:uint = 0;

	var newColorArray:Array = new Array();

	newColorArray = [];
	
	//指定された範囲の色を取る
	for (var i:int = 0; i < w; i++)
	{
		for (var j:int = 0; j < h; j++)
		{
			newColor = this.bmd.getPixel32(p.x + i, p.y + j);
			newAlpha += newColor >> 24 & 0xff;
			newRed += newColor >> 16 & 0xff;
			newGreen += newColor >> 8 & 0xff;
			newBlue += newColor & 0xff;
			
			newColorArray.push(newColor);
		}
	}
	
	//各チャンネルの平均を出しておく
	newAlpha = Math.floor(newAlpha / (w * h));
	newRed = Math.floor(newRed / (w * h));
	newGreen = Math.floor(newGreen / (w * h));
	newBlue = Math.floor(newBlue / (w * h));
	newColor = (newAlpha << 24 | newRed << 16 | newGreen << 8 | newBlue);

	//色の標準偏差をとる
	var alphaHensa:Number=0;
	var redHensa:Number=0;
	var greenHensa:Number=0;
	var blueHensa:Number=0;
	
	for(var colorIndex:int = 0; colorIndex < newColorArray.length; colorIndex++)
	{
		//アルファ
		alphaHensa += (newColorArray[colorIndex]>>24 & 0xff - newAlpha)*(newColorArray[colorIndex]>>24 & 0xff - newAlpha);
		//赤
		redHensa += (newColorArray[colorIndex]>>16 & 0xff - newRed)*(newColorArray[colorIndex]>>16 & 0xff - newRed);
		//緑
		greenHensa += (newColorArray[colorIndex]>>8 & 0xff - newGreen)*(newColorArray[colorIndex]>>8 & 0xff - newGreen);
		//青
		blueHensa += (newColorArray[colorIndex] & 0xff - newBlue)*(newColorArray[colorIndex] & 0xff - newBlue);
	}
	alphaHensa = Math.sqrt(alphaHensa/(w * h));
	redHensa = Math.sqrt(redHensa/(w * h));
	greenHensa = Math.sqrt(greenHensa/(w * h));
	blueHensa = Math.sqrt(blueHensa/(w * h));
	
	//偏差がしきい値以下の場合、つまり色のバラつきが少ない場合
	//そのRectangleは平均値で塗りつぶして終了
	if(alphaHensa < hensaThreshold && redHensa < hensaThreshold && greenHensa < hensaThreshold && blueHensa < hensaThreshold)
	{
		this.pbmd.fillRect(new Rectangle(p.x,p.y,w,h), newColor);
		drawRectangle(p.x,p.y,w,h);
	}
	else if(endFlg)
	{
		//wまたはhが小さすぎた場合も終了
		this.pbmd.fillRect(new Rectangle(p.x,p.y,w,h), newColor);
		drawRectangle(p.x,p.y,w,h);
	}
	else
	{
		//偏差がしきい値以上の場合つまり色のバラつきが大きい場合は
		//さらに2分割して再起処理にかける。
		var newW:int;
		var newW2:int;
		var newH:int;
		var newH2:int;
		var newPoint:Point;
		if(w>h)
		{
			newW = Math.floor(w/2);
			newW2 = Math.round(w/2);
			newH = h;
			newH2 = h;
			newPoint = new Point(p.x+newW,p.y);
		}
		else
		{
			newW = w;
			newW2 = w;
			newH = Math.floor(h/2);
			newH2 = Math.round(h/2);
			newPoint = new Point(p.x, p.y+newH);
		}
		startMosaic(p,newW,newH);
		startMosaic(newPoint,newW2,newH2);
	}
}

private function drawRectangle(x:Number,y:Number,w:Number,h:Number):void
{
	//確定した四角形を枠取りする
	for(var i:int = 0; i < w; i++)
	{
		this.pbmd.setPixel(x+i,y,this.rectColor);
		this.pbmd.setPixel(x+i,y+h,this.rectColor);
	}
	for(var j:int = 0; j < h; j++)
	{
		this.pbmd.setPixel(x,y+j,this.rectColor);
		this.pbmd.setPixel(x+w,y+j,this.rectColor);
	}
}

private function onMosaicLoadComp(evt:Event):void
{
	this.rl.removeEventListener(Event.COMPLETE, onMosaicLoadComp);
	this.rl.scaleX = this.rl.scaleY = 1;
	this.rl.scaleX = this.rl.scaleY = Math.min(this.width / this.rl.width, this.height / this.rl.height);
}


private function onSaveBtnClick():void
{
	//画像を保存
	if (this.mosaic.length > 0)
	{
		this.fr.save(this.mosaic, "mosaic.png");
	}
	else
	{
		//mosaicしてね。
	}
}

			
		]]>
	</mx:Script>
	<mx:Image id = "image" />

	<mx:HBox paddingTop = "10" paddingLeft = "10">
		<mx:Button id = "browseBtn" label = "参照" width="50" click = "onBrowseBtnClick();" />
		<mx:TextInput id = "time" text = "5" width = "30" />
		<mx:Label text = "←粗さ" />
		<mx:TextInput id = "thr" text = "50" width = "30" />
		<mx:Label text = "←しきい値0〜127" />
		<mx:Button id = "createBtn" label = "モザイク!" click = "onMosaicBtnClick();" />
		<mx:Button id = "saveBtn" label = "保存" click = "onSaveBtnClick();" />
	</mx:HBox>

</mx:Application>

Forked